top of page

Phonograph (Stereo)

Using the vibrato object [link], a couple of types of noise generation and some extreme high pass filtering to hopefully get somewhere close to a convincing simulation of an old (and I mean old, like 1920s style) record player.


note: "chorus1" in the GUI diagram was a placeholder for the vibrato object


The noise generation is made up of band-passed pink noise and pseudo-random clicking. The clicking comes from three square wave LFOs (made with the waveform object) fed into a couple of XOR gates using the combine object to create odd patterns. It's then high-pass filtered significantly, with the HPF cutoff frequency being randomised within a set range. The volume of the clicking is also randomised and I've even added a timer to change both of these parameters at random intervals.


The toggle switch in this sketch selects between 3 different highpass & lowpass settings on the guitar signal, kind of lowpass/bandpass/highpass. These options generally skew towards high-pass filters which (to my ears) are more reflective of a grammaphone.


I've attempted to add a needle skip effect with footswitch, so pressing it pulls the needle off the record by maxing out the vibrato depth before the effect goes silent. Releasing the footswitch then does this in reverse. It's not quite there yet. If it's going to sound convincing I really need the ability to reset the vibrato LFO phase to 0 so I can time the pitch bends. If I can figure out how to do this I could also modulate the pink noise volume in time with the vibrato LFO, adding to the illusion of a spinning record.


The needle skip was inspired (shamelessly stolen) from the amazing 'Melusine' pedal by Hexe.


There are many improvements to be made, and I've included plenty of comments in the sketch so it can easily be tweaked to your liking.



#define LED 3
#include <Bounce.h>
#include <vibrato.h>

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=253.08334350585938,376.0833435058594
AudioSynthWaveform       waveform6; //xy=498.41668701171875,544.0833740234375
AudioSynthWaveform       waveform5; //xy=499.41668701171875,496.0833740234375
AudioSynthWaveform       waveform4; //xy=501.75,446.0833435058594
AudioSynthWaveform       waveform3; //xy=509.75,328.0833435058594
AudioSynthWaveform       waveform2; //xy=510.75,280.0833435058594
AudioSynthWaveform       waveform1;      //xy=513.0833129882812,230.08331298828125
AudioEffectHighQualityVibrato     vibrato1;
AudioEffectHighQualityVibrato     vibrato2;
AudioEffectDigitalCombine combine4; //xy=674.75,525.0833129882812
AudioEffectDigitalCombine combine3; //xy=680.75,469.0833435058594
AudioSynthNoisePink      pink2; //xy=683.75,605.0833129882812
AudioEffectDigitalCombine combine2; //xy=689.75,307.0833435058594
AudioEffectDigitalCombine combine1;       //xy=691.0833129882812,248.08334350585938
AudioSynthNoisePink      pink1;          //xy=702.0833282470703,380.0833282470703
AudioFilterBiquad        biquad6; //xy=746.75,694.0833129882812
AudioFilterBiquad        biquad3; //xy=750.75,107.08333587646484
AudioFilterBiquad        biquad5; //xy=838.75,596.0833129882812
AudioFilterBiquad        biquad1;        //xy=846.0833129882812,275.0833435058594
AudioFilterBiquad        biquad2; //xy=846.75,370.0833435058594
AudioFilterBiquad        biquad4; //xy=846.75,502.0833435058594
AudioMixer4              mixer2; //xy=1091.75,537.0833129882812
AudioMixer4              mixer1; //xy=1111.083251953125,294.0833435058594
AudioOutputI2S           i2s2;           //xy=1282.083251953125,413.0833435058594
AudioConnection          patchCord1(i2s1, 0, vibrato1, 0);
AudioConnection          patchCord2(i2s1, 1, vibrato2, 0);
AudioConnection          patchCord3(waveform6, 0, combine4, 1);
AudioConnection          patchCord4(waveform5, 0, combine3, 1);
AudioConnection          patchCord5(waveform4, 0, combine3, 0);
AudioConnection          patchCord6(waveform3, 0, combine2, 1);
AudioConnection          patchCord7(waveform2, 0, combine1, 1);
AudioConnection          patchCord8(waveform1, 0, combine1, 0);
AudioConnection          patchCord9(vibrato2, biquad6);
AudioConnection          patchCord10(vibrato1, biquad3);
AudioConnection          patchCord11(combine4, biquad4);
AudioConnection          patchCord12(combine3, 0, combine4, 0);
AudioConnection          patchCord13(pink2, biquad5);
AudioConnection          patchCord14(combine2, biquad1);
AudioConnection          patchCord15(combine1, 0, combine2, 0);
AudioConnection          patchCord16(pink1, biquad2);
AudioConnection          patchCord17(biquad6, 0, mixer2, 0);
AudioConnection          patchCord18(biquad3, 0, mixer1, 0);
AudioConnection          patchCord19(biquad5, 0, mixer2, 2);
AudioConnection          patchCord20(biquad1, 0, mixer1, 1);
AudioConnection          patchCord21(biquad2, 0, mixer1, 2);
AudioConnection          patchCord22(biquad4, 0, mixer2, 1);
AudioConnection          patchCord23(mixer2, 0, i2s2, 1);
AudioConnection          patchCord24(mixer1, 0, i2s2, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=1276.083251953125,667.0833129882812
// GUItool: end automatically generated code

Bounce footswitch = Bounce(0, 50);  // debounce the footswitch
Bounce D1 = Bounce(1, 50);          // debounce the toggle switch
Bounce D2 = Bounce(2, 50);          // "  "  "  "  "  "  "  "  "

// this section includes the function to check the toggle position
bool right;
bool middle;
bool left;
void checkToggle () {               // this is our function to check toggle position...
D1.update();  D2.update();          // check digital inputs connected to toggle (can delete I think)
if(digitalRead(1) && !digitalRead(2))   {right = 1; middle = 0; left = 0;}    // toggle is right
if(digitalRead(1) && digitalRead(2))  {right = 0; middle = 1; left = 0;}      // toggle is in the middle
if(!digitalRead(1) && digitalRead(2))   {right = 0; middle = 0; left = 1;}    // toggle is left
}

// noise volumes
float noisevol;                                               // variable to control pink noise volume
float clickvol;                                               // variable to control click volume

// click volume / hpf randomisation
int clickhpf = 8000;                                          // variable for storing randomised hpf cutoff for the clicks
unsigned int randomtime = 0;                                  // this will dictate the number of milliseconds until the next click volume change
unsigned long currentMillis = millis();                       // this acts as a millisecond timer

// vibrato
float rate = 1;
float depth = 5;

//needle skip
byte skipping = 0;                                            // will be 1 when in process of skipping pre-silence, 2 post-silence, 0 in normal non-skipping process

void setup() {
 
  AudioMemory(40); // the "40" represents how much internal memory (in the Teensy, not the external RAM chip) is allotted for audio recording. It is measured in sample blocks, each providing 2.9ms of audio.
  sgtl5000_1.enable();    // this turns on the SGTL5000, which is the audio codec on the audio board
  sgtl5000_1.volume(1);   // this sets the output volume (it can be between 0 and 1)
  sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN); // selects the audio input, we always use Line In
  analogReadResolution(12); // configure the pots to give 12 bit readings
  pinMode(0, INPUT_PULLUP); // internal pull-up resistor for footswitch
  pinMode(1, INPUT_PULLUP); // internal pull-up resistor for toggle
  pinMode(2, INPUT_PULLUP); // internal pull-up resistor for toggle
  pinMode(3, OUTPUT);       // pin 3 (the LED) is an output;
  Serial.begin(9600);       // initiate the serial monitor. USB is always 12 Mbit/sec

  // click generator
  waveform1.begin(1, 0.82, WAVEFORM_SQUARE);                  // increasing "0.xx" in each of these three will increase the regularity of the clicks... 
  waveform2.begin(1, 0.42, WAVEFORM_SQUARE);                  // ... these are prime numbers, intended to sound more random, for what that's worth
  waveform3.begin(1, 1.29, WAVEFORM_SQUARE);
  combine1.setCombineMode(AudioEffectDigitalCombine::XOR);
  combine2.setCombineMode(AudioEffectDigitalCombine::XOR);
  biquad1.setLowpass(0, 9800, 0.3);                           // 9800 is the lpf cutoff for the clicks
  biquad1.setHighpass(0, 9000, 0.3);                          // 9000 is the hpf cutoff for the clicks - GETS RANDOMISED LATER!
  mixer1.gain(1,0.5);
  // stereo
  waveform4.begin(1, 1.87, WAVEFORM_SQUARE);                  // increasing "0.xx" in each of these three will increase the regularity of the clicks... 
  waveform5.begin(1, 0.43, WAVEFORM_SQUARE);                  // ... these are prime numbers, intended to sound more random, for what that's worth
  waveform6.begin(1, 0.24, WAVEFORM_SQUARE);
  combine1.setCombineMode(AudioEffectDigitalCombine::XOR);
  combine2.setCombineMode(AudioEffectDigitalCombine::XOR);
  biquad4.setLowpass(0, 9800, 0.3);                           // 9800 is the lpf cutoff for the clicks
  biquad4.setHighpass(0, 9000, 0.3);                          // 9000 is the hpf cutoff for the clicks - GETS RANDOMISED LATER!
  mixer2.gain(1,0.5);


  // noise generator
  pink1.amplitude(0.2);                                       // initial volume of pink noise, probably best not to change. it has other volume controls elsewhere
  biquad2.setLowpass(0,5000,0.5);                             // 8800 is the lpf cutoff for the pink noise
  biquad2.setHighpass(1,5500,0.5);                            // 8800 is the hpf cutoff for the pink noise
  //mixer1.gain(2,0.2);                                         
  // stereo
  pink2.amplitude(0.2);                                       // initial volume of pink noise, probably best not to change. it has other volume controls elsewhere
  biquad5.setLowpass(0,5000,0.5);                             // 8800 is the lpf cutoff for the pink noise
  biquad5.setHighpass(1,5500,0.5);                            // 8800 is the hpf cutoff for the pink noise


  // guitar signal filtering
  biquad3.setHighpass(0, 1100, 1);                          // 3500 is the hpf cutoff for the guitar signal
  biquad3.setLowpass(1, 2000, 1.6);                           // 7500 is the lpf cutoff for the guitar signal
  mixer1.gain(0, 3);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf
  // stereo
  biquad6.setHighpass(0, 1100, 1);                          // 3500 is the hpf cutoff for the guitar signal
  biquad6.setLowpass(1, 2000, 1.6);                           // 7500 is the lpf cutoff for the guitar signal
  mixer2.gain(0, 3);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf


  

  
}

void loop() {

  // noise volumes
  noisevol = (float) analogRead(A2) / 10000;                   // increasing 10000 will reduce max pink noise vol
  clickvol = (float) analogRead(A3) / 18000;                   // increasing 16000 will reduce max click vol
  mixer1.gain(2,noisevol);                                        
  mixer1.gain(1,clickvol);
  // stereo
  mixer2.gain(2,noisevol);                                        
  mixer2.gain(1,clickvol);
  

  // randomise volume amd hpf of clicks
  if(!skipping && (millis() - currentMillis >= randomtime))    
  {
    mixer1.gain(1, (float) (rand() % 100) / 500 );            // increase 500 for quieter clicks on average
    mixer2.gain(1, (float) (rand() % 100) / 500 );            // increase 500 for quieter clicks on average
    clickhpf = (rand() % 5000) + 5000;                        // generate new random click hpf cutoff (7000hz - 9000hz) 
    biquad1.setHighpass(0, clickhpf, 0.3);                    // set the new hpf cutoff for the clicks
    biquad4.setHighpass(0, clickhpf, 0.3);                    // set the new hpf cutoff for the clicks
    randomtime = rand() % 50;                                 // increase 50 to increase the maximum number of milliseconds until the next volume and hpf change (time period is randomised)
    currentMillis = millis();                                 // reset timer
  }

  // vibrato
  if(!skipping) {
    rate = analogRead(A0) / 600;                                // read pot A0 to get vibrato rate
    depth = analogRead(A1) / 400;                               // read pot A1 to get vibrato depth
    vibrato1.modulation(rate, depth);                         // set vibrato rate and depth
    vibrato2.modulation(rate, depth);                         // set vibrato rate and depth
  }

  // toggle switch selects between different frequency bands for the guitar signal
  checkToggle ();
  if(left){
    biquad3.setHighpass(0, 20, 1);                          // 3500 is the hpf cutoff for the guitar signal
    biquad3.setLowpass(1, 1500, 1);                           // 7500 is the lpf cutoff for the guitar signal
    mixer1.gain(0, 2);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf
  // stereo
    biquad6.setHighpass(0, 20, 1);                          // 3500 is the hpf cutoff for the guitar signal
    biquad6.setLowpass(1, 1500, 1);                           // 7500 is the lpf cutoff for the guitar signal
    mixer2.gain(0, 2);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf
  }
  else if(middle){
    biquad3.setHighpass(0, 1100, 1);                          // 3500 is the hpf cutoff for the guitar signal
    biquad3.setLowpass(1, 2000, 1.2);                           // 7500 is the lpf cutoff for the guitar signal
    mixer1.gain(0, 3);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf
  // stereo
    biquad6.setHighpass(0, 1100, 1);                          // 3500 is the hpf cutoff for the guitar signal
    biquad6.setLowpass(1, 2000, 1.2);                           // 7500 is the lpf cutoff for the guitar signal
    mixer2.gain(0, 3);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf                                        // 5 = guitar signal 
  }
  else if(right){
    biquad3.setHighpass(0, 1800, 1.4);                          // 3500 is the hpf cutoff for the guitar signal
    biquad3.setLowpass(1, 3200, 1.5);                           // 7500 is the lpf cutoff for the guitar signal
    mixer1.gain(0, 3);                                         // guitar signal gets a volume boost here to compensate for the extreme hpf
  // stereo
    biquad6.setHighpass(0, 1800, 1.4);                          // 3500 is the hpf cutoff for the guitar signal
    biquad6.setLowpass(1, 3200, 1.5);                           // 7500 is the lpf cutoff for the guitar signal
    mixer2.gain(0, 3);                                           // 2 = guitar signal volume 
  }

  // footswitch needle skip
  footswitch.update();
  if(footswitch.fallingEdge()) {                                // pre-silence skip-routine
    vibrato1.modulation(5, 10);                                 // set specific vibrato rate and depth (5hz, so half an LFO cycle will be 100ms ... and max depth)
    currentMillis = millis();                                   // reset timer
    skipping = 1;
  }

  if(skipping == 1 && (millis() - currentMillis >= 100)) {      // wait for vibrato pitch bend to peak...                             need to be able to reset vibrato phase
    while(!digitalRead(0)) {                                    // silence skip-routine, while footswitch held
      mixer1.gain(0,0);                                         // mute output
      mixer1.gain(1,0);
      mixer1.gain(2,0);
    }    
    mixer1.gain(0,10);                                          // turn output volume back up
    mixer1.gain(1,0.5);
    mixer1.gain(2,0.2);                        
    skipping = 2;                                               // enter post silence mode 
    currentMillis = millis();                                   // reset timer 
  }

  if(skipping == 2 && (millis() - currentMillis >= 100)) {      // post-silence skip-routine
    vibrato1.modulation(rate, depth*3);                         // set vibrato rate and depth back to pot settings
    skipping = 0;                                               // exit skipping routine, return to normal
  }
    
}




Comments


Recent Posts
Search By Tags
Glowfly_Logo_RGB_inv.png
bottom of page