New polyhonic Teensy DIY Sampler

Hello everyone.
Because of the defective Teensy4 modules there are now changes in the circuit diagram of the Degenerator2.
There are changes to the Degenerator2 power supply circuit diagram. There is now only one +3.3 V power supply.
The +3.3 V power supply of the Teensy4 modules is no longer used.
@Rolfdegen: I think you meant "damaged" rather than "defective" on your Teensy4 modules. You wouldn't want a future reader to get the wrong impression...the T4s were working fine when you received them.

Mark J Culross
KD5RXT
 
Yes, that's right. I mean, damaged. I hope it doesn't happen again. I checked all the solder joints and connections again and found no errors. The new Teensys are arriving on Monday. I'm excited.
 
New Teensy's are arriving tomorrow, and I can continue working. Hopefully without damaged the Teensys. Why they had a short circuit on the TxD pin and the 3.3V pin remains a mystery to me :unsure:
 
Houston, we don't have a problem :)
The new Teensys work very well. Sound is great. Now I can continue working undisturbed.
 
News..
I've made some hardware changes. The 24 buttons are now controlled by two MCP23017 16-bit I/O expanders.
The encoders have been replaced with Alpha 360-degree endless rotary potentiometers.
The seven Alpha potentiometers are controlled by a 74HC4067 16:1 analog multiplexer and two analog inputs on the Teensy4.1.
Compared to encoders, the 360° Alpha potentiometers have the advantage that no time-critical query is required from the processor.
I've removed the seven ATtiny chips that were responsible for controlling the encoders, buttons, and potentiometers.

MPC23017 for key scanning
Screenshot 2025-06-12 111647.png


16:1 Analog Multiplexer 74HC4067
Screenshot 2025-06-12 111918.png

Alpha 360-degree endless rotary potentiometers
Screenshot 2025-06-12 112013.png
 
Hello friends,
The new Alpha potentiometers are a bit too stiff for me personally. For this reason, I was looking for a simple solution to make them a bit smoother.
Unscrewing or opening the Alpha potentiometers was out of the question, as I wanted to spare people who wanted to replicate the Degenerator2 the process. But a coincidence came to my aid. When I soldered the Alpha potentiometers in place, they became a little easier to turn. Once the potentiometer cooled down, they became stiff again. So I thought, just heat the thing. I quickly found a 330 ohm resistor, and the existing 12V power supply in the degenerator was available. I simply soldered the resistor over the Alpha potentiometer and connected it to the 12V supply. The resistor heats up and warms the Alpha potentiometer. Compared to a non-heated Alpha potentiometer, the heated Alpha potentiometer turns much more easily. The tactile feel is now similar to that of the encoders in the Blofeld.

Addendum: A dab of thermal paste improves thermal contact with the potentiometer ;-)

Alpha_470R.jpg


Addendum:
I've experimented a bit more and found that 470 ohm resistors are optimal for the Alpha potentiometers.
The 330 ohm resistors had overheated and made the Alpha potentiometers too easy to turn.
I used SG100X from Silverbead as the thermal adhesive.
 
Heated potentiometers... "The best ideas are always the unusual ones," someone once said.
This gives the Degenerator 2 an exceptional unique selling point ;)
 
So... the hardware is in place. Now comes the tricky part. Reading the Alpha potentiometers. There are seven in total. I use an analog input on the Teensy for this. The potentiometers are read via the 74HC4067 analog multiplexer. This has 16 analog inputs and three address lines for selecting the input channels.

Screenshot 2025-06-14 174018.png

20250614_172202.jpg
 
So... the hardware is in place. Now comes the tricky part. Reading the Alpha potentiometers. There are seven in total. I use an analog input on the Teensy for this. The potentiometers are read via the 74HC4067 analog multiplexer. This has 16 analog inputs and three address lines for selecting the input channels.

Make sure to read the datasheet for the 74HC4067 analog mux carefully. It works great, but you must respect the required settling time after setting the address lines & before reading the output.

Mark J Culross
KD5RXT
 
Reading 360° Endless Pot with library for eliminating noise in analogRead inputs

C:
#include <Arduino.h>
#include <ResponsiveAnalogRead.h>

// 74HC4067 Analog Multiplexer for Pots
int PotSel_1 = 2;
int PotSel_2 = 3;
int PotSel_3 = 4;
int PotSel_4 = 5;

// define the pin you want to use
const int ANALOG_PIN = A0;


// make a ResponsiveAnalogRead object, pass in the pin, and either true or false depending on if you want sleep enabled
// enabling sleep will cause values to take less time to stop changing and potentially stop changing more abruptly,
//   where as disabling sleep will cause values to ease into their correct position smoothly and more accurately
ResponsiveAnalogRead analog(ANALOG_PIN, true);
ResponsiveAnalogRead analog1(ANALOG_PIN, true);

// Setup -------------------------------------------------------
void setup()
{
    // init PotSel 74HC4067 Analog Multiplexer
    pinMode(PotSel_1, OUTPUT);
    pinMode(PotSel_2, OUTPUT);
    pinMode(PotSel_3, OUTPUT);
    pinMode(PotSel_4, OUTPUT);
    digitalWrite(PotSel_1, LOW);
    digitalWrite(PotSel_2, LOW);
    digitalWrite(PotSel_3, LOW);
    digitalWrite(PotSel_4, LOW);
    // init ADC Input for Pots
    analogReadResolution(10);
    analog.setAnalogResolution(1024);
   
}

// read Pots ---------------------------------------------------
void read_Pot()
{
    if (readPot_Timer > 5)
    {
        readPot_Timer = 0;
       
        // select Pot
        digitalWrite(PotSel_1, LOW);
        digitalWrite(PotSel_2, LOW);
        digitalWrite(PotSel_3, LOW);
        digitalWrite(PotSel_4, LOW);

        // read channel 1
        analog.update();
        // read channel 2
        digitalWrite(PotSel_1, HIGH);
        analog1.update();

        if (analog.hasChanged() && analog1.hasChanged())
        {
            int value_A = analog.getValue();    // value_A  0 - 1022
            int value_B = analog1.getValue();   // value_B  0 - 1022

            Serial.print(value_A);
            Serial.print("  ");
            Serial.print(value_B);
            Serial.print("  ");

            // Compute current angle and dial position
            double fx = ((double)value_A / 511.5) - 1;   // range -1 to +1
            double fy = ((double)value_B / 511.5) - 1;
            double angle = atan2(fx, fy)/PI;               // range -pi to +pi
            Serial.println(angle);
           //int dialPosition = map(angle, -PI, PI, 0, 128);
        }
       
    }
}

// Loop ---------------------------------------------------
void loop()
{
   read_Pot();

 
How can I calculate the parameter value for a volume level, for example :unsure:
The volume is set to 50% in the menu. I turn the potentiometer to the right and increase it to 90%
 
Hallo..
I found a solution using the map() function. StartPos is 0 or the last displayed parameter value in the menu. EndPos is the maximum end value of the potentiometer. If the potentiometer makes a 360 degree rotation then the value is from 0 - 127;

C:
// read Pots ---------------------------------------------------
void read_Pot()
{
    if (readPot_Timer > 5)
    {
        readPot_Timer = 0;

        // select Pot
        digitalWrite(PotSel_1, LOW);
        digitalWrite(PotSel_2, LOW);
        digitalWrite(PotSel_3, LOW);
        digitalWrite(PotSel_4, LOW);

        // read channel 1
        analog.update();
        // read channel 2
        digitalWrite(PotSel_1, HIGH);
        analog1.update();

        if (analog.hasChanged() && analog1.hasChanged())
        {
            int value_A = analog.getValue();  // value_A  0 - 1022
            int value_B = analog1.getValue(); // value_B  0 - 1022

            Serial.print(value_A);
            Serial.print("  ");
            Serial.print(value_B);
            Serial.print("  ");

            // Compute current angle and dial position
            double fx = ((double)value_A / 511.5) - 1; // range -1 to +1
            double fy = ((double)value_B / 511.5) - 1;
            double angle = atan2(fx, fy); // range -pi to +pi
            Serial.print(angle);
            Serial.print("  ");
            int StartPos = 0;
            int EndPos = 127;
            int dialPosition = map(angle, -PI, PI, StartPos, EndPos);
            Serial.println(dialPosition);
        }
    }
}
 
Hello everyone :)
I hope you had a nice sunny day. I worked a little and then went for a ebike ride.
I also continued working on Degenerator 2 and programmed the query function for the "heated" Alpha potentiometers ;-)
It's not quite finished yet, but in principle, it's already working quite well, as you can see in the video below.


Greetings from germany. Rolf
 
  • Like
Reactions: Pio
Read 360° endless Potentiometer from 0 - 1023
I use the ResponsiveAnalogRead.h library for prevent the noise of the potentiometers.

ResponsiveAnalogRead is an Arduino library for eliminating noise in analogRead inputs without decreasing responsiveness. It sets out to achieve the following:
  1. Be able to reduce large amounts of noise when reading a signal. So if a voltage is unchanging aside from noise, the values returned should never change due to noise alone.
  2. Be extremely responsive (i.e. not sluggish) when the voltage changes quickly.
  3. Have the option to be responsive when a voltage stops changing - when enabled the values returned must stop changing almost immediately after. When this option is enabled, a very small sacrifice in accuracy is permitted.
  4. The returned values must avoid 'jumping' up several numbers at once, especially when the input signal changes very slowly. It's better to transition smoothly as long as that smooth transition is short.

C:
#include <ResponsiveAnalogRead.h>


// 74HC4067 Analog Multiplexer for Pots
int PotSel_1 = 2;
int PotSel_2 = 3;
int PotSel_3 = 4;
int PotSel_4 = 5;
int analogReadVal_A = 0;
int analogReadVal_B = 0;
int analogRead_prevVal = 0;
int prevValue = -1;
int potValue = 0;
boolean analogReadUpdate_A = false;
boolean analogReadUpdate_B = false;

// define the pin you want to use
const int ANALOG_PIN = A0;
// make a ResponsiveAnalogRead object, pass in the pin, and either true or false depending on if you want sleep enabled
// enabling sleep will cause values to take less time to stop changing and potentially stop changing more abruptly,
// where as disabling sleep will cause values to ease into their correct position smoothly and more accurately
ResponsiveAnalogRead analog1(ANALOG_PIN, true);
ResponsiveAnalogRead analog2(ANALOG_PIN, true);



// read 360° endless Pots --------------------------------------
void read_Pot()
{
    if (readPot_Timer > 21) // 21ms
    {
        readPot_Timer = 0;

        // Select two analog inputs on 74HC4067 multiplexer
        digitalWrite(PotSel_1, LOW);
        digitalWrite(PotSel_2, HIGH);
        digitalWrite(PotSel_3, LOW);
        digitalWrite(PotSel_4, HIGH);
        analog1.update();
        digitalWrite(PotSel_1, HIGH);
        digitalWrite(PotSel_2, HIGH);
        digitalWrite(PotSel_3, LOW);
        digitalWrite(PotSel_4, HIGH);
        analog2.update();

        // if analog inputs change than calc potValue
        if (analog1.hasChanged() && analogReadUpdate_A == false)
        {
            analogReadVal_A = analog1.getValue(); // value_A  0 - 1023
            analogReadUpdate_A = true;
            {
                if (analog1.hasChanged() && analogReadUpdate_B == false)
                {
                    analogReadVal_B = analog2.getValue(); // value_B  0 - 1023
                    analogReadUpdate_B = true;
                    {

                        if (analogReadUpdate_A == true && analogReadUpdate_B == true)
                        {
                            // Compute current angle and dial position
                            double fx = ((double)analogReadVal_A / 511.5) - 1; // range -1 to +1
                            double fy = ((double)analogReadVal_B / 511.5) - 1;
                            double angle = atan2(fx, fy); // range -pi to +pi
                            int newValue = (angle + M_PI) * (float)1023 / (2.0 * M_PI);
                            int delta = 0;
                            int Pot_ID = 1;
                       
                            // calc delta and steps for potValue
                            delta = (newValue - prevValue);
                            if (delta > 511 || delta < -511)
                            {
                                delta = 0;
                            }
                            potValue += delta;

                            if (potValue < 0)
                            {
                                potValue = 0;
                            }          
                            else if (potValue > 1023)
                            {
                                potValue = 1023;
                            }
                       
                            // save current potValue and call Pot handle
                            if (encoder_last_value[Mixer_menu][Pot_ID] != potValue)
                            {
                                Pot_handle(Pot_ID, potValue);
                                encoder_last_value[Mixer_menu][Pot_ID] = potValue;
                            }
                            // save previous potValue
                            prevValue = newValue;

                            // clear update flags
                            analogReadUpdate_A = false;
                            analogReadUpdate_B = false;
                        }
                    }
                }
            }
        }
    }
}
 
Last edited:
A little Fairlight CMI feelings.. :love:

Degenerator 2 play Fairlight sample. Setting the loop points in the "SARARR" sample from the Fairlight wasn't easy.
To accomplish this, I used the program Endless WAV by Björn Bojahr.


Endless WAV
Endless Wav.png
 
Next steps...
I'll now start working on the file system to save and load patch and sample data to the SD card.
I need to keep a few things in mind: The SD card contains a folder for the patch data and a second folder for the sample library.
When I create a new patch using a sample from the library and later save it to the SD card,
a copy of the sample is saved in the patch folder. This ensures that the patch still works even if the sample library is changed, for example, by deleting or renaming samples.

Ordner.jpg
 
Back
Top