Microtuning Synthesizer (Scala files)

Hi there,
Iv'e build a microtonal synthesisers that uses the Scala files for alternatives tuning systems using MaxMSP programme

I would like to build a synthesizer using the scala files but to be all hardware without using the computer.

I understand that for audio stuff it will be better using Teensy over Arduino? this for its better memory size(?)

Is someone build a synth that can play microtonal scales? Any links or idea where should I start?

I guess I will need to build a polyphonic sound engine to play the scales and I will need to store those scales (it is rather a cents values or ratios values) on an external sd card ?

Thanks for any tips!

For start: What are my options to read a Scala files in arduino (.scl) - do I need to convert it first to a text file?

The Scala scale (.scl) file is already a text file:

https://www.huygens-fokker.org/scala/scl_format.html

Note: Some of the example 'cent' values are showing 9 significant digits. On most basic Arduinos the 'double' and 'float' types are the same and can only hold about 6.8 significant digits (the 7th significant digit is correct only 80% of the time). You might need a processor with a true 'double' type.

What processor could it be? Uno is to low for that?
I might need to use Teensy 4.2 ?

Spurious precision due to being printed out that way, nothing to worry about.

Using double precision for a frequency might be appropriate for an atomic frequency standard or a pulsar, but not for a musical scale - rounding to the nearest cent is perfectly fine.

I have this file data:

const char scale1 [] = " ! DORIAN_PENT.scl
!
Schlesinger's Dorian Harmonia in the pentachromatic genus                       
 7
!
 55/53
 11/10
 11/8
 11/7
 55/34
 22/13
 2/1
"

how can I extract the name of the scale(first line) into one variable, the description of the scale (third line) into another variable, the number of notes (fourth line) into another variable and the ratios (sixth line until the end) into another variable array that will be already in hertz after multiplying by an fundamental frequency of 261.6 hertz (middle C (midi 60)).

edit: so if to make the task simple: I would like to separate the long string into 4 strings:

  1. the name of the scale (first line in scale1 string)
  2. the description of the scale (third line)
  3. number of notes in scale (fourth line)
  4. ratios in scale (sixth line until end of string)

each of those I then would like to store to a variable and make use of it later on.

Can I iterate over specific lines in Arduino?

You would read each line and decide what to do with each. The first non-comment line is the name. The second non-comment line is the number of notes. All further non-comment lines are notes.

If I deciding to go with Teensy 4.0 + Teensy Audio Board adapter - Can I use the Audio Board build in micro SD card to read the Scala files?

https://www.pjrc.com/store/teensy3_audio.html

Yes.

I have few CD4051 multiplexers. Can I it instead of buying 74H4051?

What are my option for connecting an old Casio/Yamaha piano keyboard to a teensy? should I use a few multiplexers?

I think you mean 74HC4051?
Yes, they will work, a bit slower to switch though, but I doubt that's an issue for audio.

Thanks!
So I made my first try to use CD4051 with an arduino and 3 potentiometers.

here is the code I'm using:



const int s0 = 8;
const int s1 = 9;
const int s2 = 10;
uint16_t MUX_In = A0;
uint8_t MUX_channel[3] = {0};


void setup() {

Serial.begin(115200);

pinMode(s0, OUTPUT);
pinMode(s1, OUTPUT);
pinMode(s2, OUTPUT);
pinMode(MUX_In, INPUT);


}

void loop() {

for (int i = 0; i < 3; i++)
{
  digitalWrite(s0, HIGH && (i & B00000001));
  digitalWrite(s1, HIGH && (i & B00000010));
  digitalWrite(s0, HIGH && (i & B00000100));
  MUX_channel[i] = analogRead(MUX_In);
  Serial.print("pot ");
  Serial.print(i+1);
  Serial.print(": ");
  Serial.println(MUX_channel[i]);
}
  
}

I connected pot 1 to 4051 pin 13, pot 2 to 4051 pin 14 and pot3 to 4051 pin 15.

It seems that arduino is not reading my middle pot (pot1)

Can someone spot my mistake?

edit: pot 2 is reading the same values as pot 1

Look carefully at the third of these lines.

Thanks!

Is there better ways to make the reading from the multiplex?
Perhaps I should transfer it into a function to be more readable as my project increase over time?
edit: I did transfer it into a function:


void MUX_check()
{
  for (int i = 0; i < 3; i++)
{
  digitalWrite(s0, HIGH && (i & B00000001));
  digitalWrite(s1, HIGH && (i & B00000010));
  digitalWrite(s2, HIGH && (i & B00000100));
  MUX_channel[i] = analogRead(MUX_In);
  Serial.print("pot ");
  Serial.print(i+1);
  Serial.print(": ");
  Serial.println(MUX_channel[i]);
  delay(1000);
}
}

void loop() {

MUX_check();
  
}

also , this part of code

  digitalWrite(s0, HIGH && (i & B00000001));
  digitalWrite(s1, HIGH && (i & B00000010));
  digitalWrite(s2, HIGH && (i & B00000100));

Is still encrypted for me. is there different way to achieve the same? can someone explain those lines?

edit2:
Why the values I'm reading are only between 0 and 255? isn't it should be 0 to 1023?

I found my mistake: I declare uint8_t which can represent only numbers between 0 to 255 instead of using uint16_t.

I try to print to the serial only when a value is changed with no success:

uint16_t MUX_channel_old[numOfPots] = {0}; // new array to store old values

void MUX_check()
{
  for (int i = 0; i < numOfPots; i++)
{
  digitalWrite(s0, HIGH && (i & B00000001));
  digitalWrite(s1, HIGH && (i & B00000010));
  digitalWrite(s2, HIGH && (i & B00000100));
  MUX_channel[i] = analogRead(MUX_In);
  if(MUX_channel_old[i] != MUX_channel[i])
  {
  Serial.print("pot ");
  Serial.print(i+1);
  Serial.print(": ");
  Serial.println(MUX_channel[i]);
  delay(150);
}
 
MUX_channel_old[i] = MUX_channel[i];

}
}

edit: my code is working, is just that the value is indeed constantly changing. a small jitter of around 1-3 values. Is it happening due to noise In the rail? because I'm using breadboard?
how can I smooth that?

Yes, its a redundant way to do

  digitalWrite(s0, i & B00000001);
  digitalWrite(s1, i & B00000010);
  digitalWrite(s2, i & B00000100);

What does the part i & B00000001); means?

I understand that in order to read the values from the multiplexer we need to send to the channel selected pins:

000 to read y0
001 to read y1
010 to read y2 etc etc until
111 to read y7

so in the first iteration: i = 0

digitalWrite(s0, 0 & B00000001);
  digitalWrite(s1, 0 & B00000010);
  digitalWrite(s2, 0 & B00000100);

second iteration: i = 1

digitalWrite(s0, 1 & B00000001);
  digitalWrite(s1, 1 & B00000010);
  digitalWrite(s2, 1 & B00000100);

third iteration: i = 2

digitalWrite(s0, 2 & B00000001);
  digitalWrite(s1, 2 & B00000010);
  digitalWrite(s2, 2 & B00000100);

& is the bitwise-and operator in C. B00000010 is the same as 0b00000010, a binary constant (from C++ version 14 the 0b syntax became part of the language).

So

0b00111100 &
0b01110110 =
0b00110100

Those digitalWrite lines extract the bottom 3 bits from the number in order to feed them to the address pins of the analog switch.

I still don't understand what the program is writing to the digital pin s0/s1/s2 ?

what does digitalWrite(s0, **i & B00000001**); means?