11 March 2021

NIME Music Boi




For this project, I needed to create an interactive device using a potentiometer + 2 inputs, and a servo + speaker as outputs.

 First, let’s talk about the buttons. Originally, I was going to have them play a tune alongside the metronome, but it turns out the Circuit Playground can’t really handle that (as soon as it plays a tone, everything else stops). So currently the behavior is such that as soon as you press the button, it plays a tune – and that tune may be modified, depending on where you have the potentiometer set. The tune is played by calling a series of functions, and those functions have values for pitch and duration that can then be modified by the potentiometer. I keep talking about the potentiometer, so let’s look at that.

 The potentiometer basically does 3 things. First, about 1/5th of it does nothing – it turns off the servo, and sets it’s modifying variables to zero. Second, it controls a servo – that servo oscillates back and forth at a faster and faster rate as you crank up the dial (I got it as close to matching the beat of the songs as I could, but the processor doesn’t always process at a consistent rate). Third, it sets the “modifying variables” to certain values that are mapped out to the potentiometer. Essentially, at low values, songs get played lower pitched and at a slower pace – higher values get higher pitched and faster paced.

 It all comes together in such a way that the rhythm of the servo serves as important feedback to the user, letting them know what sort of song they can expect when they push the button. Do you feel this device creates an engaging handheld musical experience? Let me know!

 Here’s the schematic, video, and code:

#include <Adafruit_CircuitPlayground.h> #include <Adafruit_Circuit_Playground.h> #include <Servo.h> /* Music Box Scaffolding * By R Mike Livingston */ Servo myservo; // create servo object to control a servo int potentiometerPin = A1; // analog pin used to connect the potentiometer int analogValue; // variable to read the value from the analog pin int mappedAnalogValue; bool songRainbow = false; bool songAllstar = false; const int beatMod = 5; // Changes how the songs speed interact with analog int durationMultiplier = 0; const int mediumSpeed = 750; // Changes how the songs pitch interact with analog int speedPitchMod = 0; int servoAngle = 90; // variable to store the servo position bool angleRise = true; void setup() { Serial.begin(9600); CircuitPlayground.begin(); myservo.attach(A2); CircuitPlayground.playTone(300,50); CircuitPlayground.playTone(500,50); CircuitPlayground.playTone(700,70); } void loop() { //..............................................................................Potentionmeter read analogValue = analogRead(potentiometerPin); //Read user value from potentiometer //..............................................................................Button Press Detection if (CircuitPlayground.leftButton() == true && songRainbow == false) { songRainbow = true; songAllstar = false; //reset other song } if (CircuitPlayground.rightButton() == true && songAllstar == false) { songAllstar = true; songRainbow = false; //reset other song } //...............................................................................Play Song if (analogValue <= 300) //in off state, songs play "normally" (no speed/pitch change) { speedPitchMod = 0; durationMultiplier = 34; } else { speedPitchMod = analogValue - mediumSpeed; //calculate and then map tone modulation values speedPitchMod = map(speedPitchMod, -mediumSpeed, 1023 - mediumSpeed, -60, 80); durationMultiplier = map(analogValue, 0, 1023, 100, 10); // map tone durations based on analog input } durationMultiplier *= beatMod; //multiplied by a const value in the variales so I can easily find the "sweet spot" in testing. PlaySong(); //...............................................................................Servo Drum if (analogValue > 300) // analog off range { RunDrum(); } //...............................................................................Console/Serial Printer ConsolePrint(); } //============================================================================================= CONSOLE PRINT void ConsolePrint() { Serial.print("Analog Value:"); Serial.print(analogValue); Serial.print(" Servo Angle:"); Serial.print(servoAngle); Serial.print(" Angle Rising?"); Serial.print(angleRise); Serial.print(" Duration Multiplier"); Serial.print(durationMultiplier); Serial.println("---"); } //============================================================================================= PLAY SONG void PlaySong() { //.............................................................................Over the Rainbow // 4C 4c 2b 1g 1a 2b c2 4C 4a 8g 4A 4f 2e 1C 1D 2e 2f 2d 1B 1C 2D 2e 4C .... Duration/Notes // 1 5 9 11 12 13 15 17 21 if(songRainbow == true) { NoteC4(4); NoteC5(4); NoteB4(2); NoteG4(1); NoteA4(1); NoteB4(2); NoteC5(2); songRainbow = false; } //.............................................................................All Star // .... Duration/Notes // if(songAllstar == true) { NoteG4(2); NoteD5(1); NoteB4(1); NoteB4(2); NoteA4(1); NoteG4(1); NoteG4(1); NoteC5(2); NoteB4(1); NoteB4(1); NoteA4(1); NoteA4(1); NoteG4(1); songAllstar = false; } } //============================================================================================= RUN DRUM void RunDrum() { if (servoAngle <= 70) //direction change variable control { angleRise = true; myservo.write(servoAngle); // move motor } if (servoAngle >= 110) { angleRise = false; myservo.write(servoAngle); // move motor } if (angleRise == true) //increment angle variable { servoAngle = servoAngle + 1; } if (angleRise == false) { servoAngle = servoAngle - 1; } //myservo.write(servoAngle); // move motor DelayFromAnalog(); } //============================================================================================= DELAY FROM ANALOG void DelayFromAnalog() { // wait an ammount of time based on analog mappedAnalogValue = map(analogValue, 300, 1023, 20, 1); delay(mappedAnalogValue); } //============================================================================================= NOTE(x)(y) //...............................................................................Individual Notes void NoteA3(int duration) { CircuitPlayground.playTone(220 + speedPitchMod,duration * durationMultiplier); } void NoteB3(int duration) { CircuitPlayground.playTone(247 + speedPitchMod,duration * durationMultiplier); } void NoteC4(int duration) { CircuitPlayground.playTone(262 + speedPitchMod,duration * durationMultiplier); } void NoteD4(int duration) { CircuitPlayground.playTone(294 + speedPitchMod,duration * durationMultiplier); } void NoteE4(int duration) { CircuitPlayground.playTone(330 + speedPitchMod,duration * durationMultiplier); } void NoteF4(int duration) { CircuitPlayground.playTone(349 + speedPitchMod,duration * durationMultiplier); } void NoteG4(int duration) { CircuitPlayground.playTone(392 + speedPitchMod,duration * durationMultiplier); } void NoteA4(int duration) { CircuitPlayground.playTone(440 + speedPitchMod,duration * durationMultiplier); } void NoteB4(int duration) { CircuitPlayground.playTone(494 + speedPitchMod,duration * durationMultiplier); } void NoteC5(int duration) { CircuitPlayground.playTone(523 + speedPitchMod,duration * durationMultiplier); } void NoteD5(int duration) { CircuitPlayground.playTone(587 + speedPitchMod,duration * durationMultiplier); } void NoteE5(int duration) { CircuitPlayground.playTone(659 + speedPitchMod,duration * durationMultiplier); }

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.