houtson
Well-known member
I ported the sample rate reducer by Emilie Gillet from Mutable Instruments Plaits (via a port to DaisySP by Ben Sergentanis) to a stand-alone Audio Library object.
There's a basic sample rate reducer in the existing library (the bit crusher effect), this one is more flexible. It uses floats so probably only for Teensy 4.x.
I think it sounds good - if anyone wants to use it, code below, I've included an example.....cheers, Paul
edit: i should say - it doesn't actually change the rate rate, it's only an effect
effect_smoothsamplerate.h
effect_smoothsamplerate.cpp
test.ino
There's a basic sample rate reducer in the existing library (the bit crusher effect), this one is more flexible. It uses floats so probably only for Teensy 4.x.
I think it sounds good - if anyone wants to use it, code below, I've included an example.....cheers, Paul
edit: i should say - it doesn't actually change the rate rate, it's only an effect
effect_smoothsamplerate.h
Code:
// Copyright 2014 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// 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 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.
//
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Sample rate reducer.
// Ported from
// - pichenettes/eurorack/plaits/dsp/fx/sample_rate_reducer.h
// - Original code written by Emilie Gillet
// to
// - DaisySP by Ben Sergentanis Jan 2021
// then
// - Teensy 4.x by Paul Feely Apr 2021
#ifndef effect_smoothsamplerate_h_
#define effect_smoothsamplerate_h_
#include <stdint.h>
#include "Arduino.h"
#include "AudioStream.h"
#include "arm_math.h"
class AudioEffectSmoothSampleRate : public AudioStream {
public:
AudioEffectSmoothSampleRate() : AudioStream(1, inputQueueArray){};
void Init();
virtual void update(void);
// process a single sample
float Process(float in);
// Set the new sample rate, works over 0-1. 1 is full quality, .5 is half sample rate, etc.
void SetFreq(float frequency);
private:
// helpers from pichenettes/eurorack/plaits/dsp/oscillator/oscillator.h
inline float ThisBlepSample(float t);
inline float NextBlepSample(float t);
inline float fclamp(float in, float min, float max);
float frequency_;
float phase_;
float sample_;
float previous_sample_;
float next_sample_;
audio_block_t *inputQueueArray[1];
};
#endif
effect_smoothsamplerate.cpp
Code:
#include "effect_smoothsamplerate.h"
#include <Arduino.h>
void AudioEffectSmoothSampleRate::Init() {
frequency_ = .2f;
phase_ = 0.0f;
sample_ = 0.0f;
next_sample_ = 0.0f;
previous_sample_ = 0.0f;
}
void AudioEffectSmoothSampleRate::update(void) {
audio_block_t *blocka;
float32_t blockFloat[AUDIO_BLOCK_SAMPLES];
blocka = receiveWritable(0);
if (!blocka) {
return;
}
// convert block of samples to floats
arm_q15_to_float(blocka->data, blockFloat, AUDIO_BLOCK_SAMPLES);
// process a block of samples
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
blockFloat[i] = Process(blockFloat[i]);
}
// convert block back to ints
arm_float_to_q15(blockFloat, blocka->data, AUDIO_BLOCK_SAMPLES);
// transmitt block
transmit(blocka);
release(blocka);
}
float AudioEffectSmoothSampleRate::Process(float in) {
float this_sample = next_sample_;
next_sample_ = 0.f;
phase_ += frequency_;
if (phase_ >= 1.0f) {
phase_ -= 1.0f;
float t = phase_ / frequency_;
// t = 0: the transition occurred right at this sample.
// t = 1: the transition occurred at the previous sample.
// Use linear interpolation to recover the fractional sample.
float new_sample = previous_sample_ + (in - previous_sample_) * (1.0f - t);
float discontinuity = new_sample - sample_;
this_sample += discontinuity * ThisBlepSample(t);
next_sample_ = discontinuity * NextBlepSample(t);
sample_ = new_sample;
}
next_sample_ += sample_;
previous_sample_ = in;
return this_sample;
}
void AudioEffectSmoothSampleRate::SetFreq(float frequency) { frequency_ = fclamp(frequency, 0.001f, 1.f); }
// helpers
float AudioEffectSmoothSampleRate::ThisBlepSample(float t) { return 0.5f * t * t; }
float AudioEffectSmoothSampleRate::NextBlepSample(float t) {
t = 1.0f - t;
return -0.5f * t * t;
}
float AudioEffectSmoothSampleRate::fclamp(float in, float min, float max) { return fmin(fmax(in, min), max); }
test.ino
Code:
#include <Audio.h>
#include <ResponsiveAnalogRead.h>
#include <SD.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <Wire.h>
#include <effect_smoothsamplerate.h>
/*
Example of sample rate reducer AudioEffectSmoothSampleRate
Expects an audioshield with an input at linein and a pot on A2
Note: uses floating points so mainly only for Teensy 4.x
*/
// GUItool: begin automatically generated code
AudioInputI2S i2s1; // xy=198,215
AudioMixer4 mixer1; // xy=404,218
AudioEffectSmoothSampleRate rateReducer; // xy=601,211
AudioOutputI2S i2s2; // xy=795,216
AudioConnection patchCord1(i2s1, 0, mixer1, 0);
AudioConnection patchCord2(i2s1, 1, mixer1, 1);
AudioConnection patchCord3(mixer1, rateReducer);
AudioConnection patchCord4(rateReducer, 0, i2s2, 0);
AudioConnection patchCord5(rateReducer, 0, i2s2, 1);
AudioControlSGTL5000 sgtl5000_1; // xy=212,422
// GUItool: end automatically generated code
const int myInput = AUDIO_INPUT_LINEIN;
const int ANALOG_PIN = A2;
ResponsiveAnalogRead analog(ANALOG_PIN, true);
uint32_t upTime = millis();
void setup() {
Serial.begin(38400);
while (!Serial && ((millis() - upTime) <= 5000))
;
AudioMemory(20);
// setup audioshield
sgtl5000_1.enable();
sgtl5000_1.inputSelect(myInput);
sgtl5000_1.volume(0.5);
// setup mixer
mixer1.gain(0, 0.5f);
mixer1.gain(1, 0.5f);
}
void loop() {
static float frequ = 0.0f;
analog.update();
if (analog.hasChanged()) {
frequ = float(analog.getValue())/1023.0f;
rateReducer.SetFreq(frequ);
Serial.printf("Frequency Multiplier : %0.3f Frequency : %0.0fHz\n", frequ, frequ * AUDIO_SAMPLE_RATE);
}
}