Sampler Rate Reducer Effect

Status
Not open for further replies.

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
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);
  }
}
 
Status
Not open for further replies.
Back
Top