Jump to content

Assigning Colours to specific tones on Arduino?

Hey, so i'm currently working with an arduino leonardo with a 321Maker college kit and I've just got done making a song using the buzzer; however, i'm having a problem doing a little light show i want to do. I'd like to assign an rgb value to each Note that i used (D4, A4, GS4. etc) but i'm having a little trouble figuring out exactly which value i actually need to use. also, should I use and if or a while statement? I'm still not super confident in this stuff and i would appreciate the help (also i cut out the bulk of the song for here just to make it look a bit better. 


#include "pitches.h";
int buzzerPin  = 5 ;  //The buzzerPin is connected to pin 5 of the Arduino.
int button1Pin = 2;   //The SW1 button is connect to pin 2 of the Arduino.
int RGBRedPin = 9;    //The red RGB LED is connected pin 9 of the Arduino.
int RGBGreenPin = 10; //The green RGB LED is connected pin 10 of the Arduino.
int RGBBluePin = 11;  //The blue RGB LED is connected pin 11 of the Arduino.



void setup() { //The Setup function runs once.
  pinMode(buzzerPin, OUTPUT);  //Setup red LED pin as an output pin.
  pinMode(button1Pin, INPUT);  //Setup button1 pin as an input pin.
  pinMode(RGBRedPin, OUTPUT);    //Setup red RGB LED pin as an output pin.
  pinMode(RGBGreenPin, OUTPUT);  //Setup green RGB LED pin as an output pin.
  pinMode(RGBBluePin, OUTPUT);   //Setup blue RGB LED pin as an output pin.
  Serial.begin(9600);
}

void loop() { //The loop function runs forever.
  if (digitalRead(button1Pin) == LOW) { //Check to see if button1 is pressed.
 
 //Opening repeating thing
 tone(buzzerPin, NOTE_D4,500);
 delay(204);
 tone(buzzerPin, NOTE_D4,500);
 delay(204);
 tone(buzzerPin, NOTE_D5,500);
 delay(204);
 tone(buzzerPin, NOTE_A4,500);
 delay(204);


}
}
       
      

 

Link to comment
Share on other sites

Link to post
Share on other sites

Maybe make a method that plays the tone as well as checks what RGB value that's supposed to be. kinda like


void toneColor(int buzzerPin, int tone, long length)
{
	tone(buzzerPin, tone, length);
    //have some process that checks what color for what tone, like maybe a switch statement?
    switch (tone)
    {
    case /*whatever you want red to be*/ : analogWrite(RGBRedPin, 255); break;
    }
    //something like that
}

 

then just replace every time you use tone() with toneColor() (could be just a simple control+f replace thing)

 

 

Specs: CPU: AMD Ryzen R7 3700X @4.4Ghz, GPU: Gigabyte RX 5700 XT, RAM: 32 GB (2x 8GB Trident Z Royal + 2x 8GB TForce Vulkan Z) @3000Mhz, Motherboard: ASRock B550m Steel Legend, Storage: 1x WD Black 1Tb NVMe (boot) + 1x Samsung 860 QVO 1Tb SSD (storage), Case: Thermaltake Core V21, Cooler: Noctua NH-D15

Link to comment
Share on other sites

Link to post
Share on other sites

if your tone is an int, you could do something silly like treat it as 4 bytes and then generate RGB value from the RGBA value that an int would represent.


To get the RGBA values out of the tone, assuming the tone is a 32 bit integer:

 


One possible simple function to return a single color field from a given tone and field identifier would then be:


All of that is wrong. I assumed that the Leonardo uses 32 bit ints. I was wrong. It uses 16 bit ints.

Edit Again: I've been thinking about it. It's bothering me that I couldn't come up with a solution better than a massive enum and a huge switch case.

The way I see it, there are two easy ways to do this. The first is to treat the 16 bit field NOTE as 3 8 bit fields. That would be the lower 8 bits, the higher 8 bits, and some middle 8 bits.

The second way would be to come up with some operation to apply the NOTE to the duration, resulting in some 32 bit value that represents both the duration and the note. Then we can extract an RGBA vector out of that, and transform that into an RGB vector.

 

The first method might look like this:

int GetColor(int note, char color)
{
  switch (color)
  {
    case 'r':
      return note & 255; // return the lower byte
      
    case 'g':
      return (note >> 8) & 255; // return the higher byte
      
    case 'b':
      return (note >> 4) & 255; // return a byte centered in note.
  }
}


The second method might look like this:

unsigned long MortonNumber(int note, unsigned long duration)
{
  // A Morton Number is a number which combines a point along N axes into
  // a single value. Provided that the resulting single value is large enough
  // to hold a unique number for all combinations of the point values, then the
  // following holds:
  // 	-A Morton Number is unique for a unique combination of inputs
  //	-When two vectors are close on a graph, their Morton Numbers are 
  //		close on a number line.
  
  unsigned long z = 0;
  
  for (int i = 0; i < sizeof(duration) * sizeof(char); i++)
    z |= (note & 1 << i) << i | (duration & 1 << i) << (i + 1);
  
  return z;
}

int GetColor(int note, unsigned long duration, char color)
{
  // This could easily be any operation that combines
  // duration and note into a long. A Morton Number is a good choice
  // for maintaining the uniqeness of Note/Duration combinations.
  unsigned long duratedNote = MortonNumber(note, duration);

  unsigned long alpha = 1 - (duratedNote & 255); // here we are using the lowest byte to be the alpha
  unsigned long noteColor = 0;
  
  switch (color)
  {
    case 'r':
      noteColor = alpha * ((duratedNote >> 8) & 255); // red is the second lowest byte
      break;
      
    case 'g':
      noteColor = alpha * ((duratedNote >> 16) & 255); // green is the second highest byte
      break;
      
    case 'b':
      noteColor = alpha * ((duratedNote >> 24) & 255); // blue is the highest byte
      break;
  }
  
  return (int)noteColor;
}


@TheUnderratedGamer I know that this second method is quite complicated, especially for a beginner (myself, I did not know what a Morton Number was until researching solutions to your problem here) but it can be worth looking into.

The very last thing you want to try to do is write an Enum with a color value for every note. That will require you to write a massive switch case statement to return the color given a note, and the two will be tightly coupled. If you are finding all of the bit twiddling in the above examples difficult to understand, you could minimize the work involved in the Enum method by only defining colors for the first octave, and then converting each note to it's first octave equivalent, armed with the knowledge that the frequency of a note in an octave is 2 * numOctave * firstOctaveFrequency for all octaves except the first one. The frequencies in the first octave are simply firstOctaveFrequency.

For more information, here are some links:


 

ENCRYPTION IS NOT A CRIME

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×