Guitar Program (advice/fixes needed)

(This is my first post, so forgive me if this is the wrong place)
I am currently writing a C++ program that will (eventually) display various music scales based on the users input of what key signature they want. My hope for this program is to create a visual reference for the user to see what frets are "ok" to play, for whatever backing track they are using. Anyway, I have the programs main functionality finished for my favorite scale. But there are some bugs/deficiencies with the program...

- user has to input the note in upper casing, otherwise program wont recognize it.

- notes that include flats or sharps have to be entered along with the equivalent note, starting with the lower notes #.(ex. G# is same as Ab, user would need to input G#/Ab (G is lower than A, so G is first))

Those are the two big hurdles i haven't been able to get around. After I find a fix for that I still need to find a way to output the discovered intervals into a "tab" format onto the screen. for example:

Drop D tuning, D Aeolian scale mapping
E|-------------------|
B|-------------------|
G|-----(ect...-------|
D|-0-2-3-5-7-8-10-12-|
A|-0-1-3-5-7-8-10-12-|
D|-0-2-3-5-7-8-10-12-|

I'll need to be able to detect what each strings note value is, what position that will be in relation to the musical scale (assuming it is apart of the scale) and what frets on that string are "acceptable".

This is a pretty big endeavor for me, as i do not consider myself to be a programmer. I'm an ECE student with only one programming class as my experience
so any input concerning the program is greatly appreciated. Whether that be any optimizing i can do(I'm sure the method I'm using to detect the tonic for the user is inefficient) or anything I can do to make the code smaller or more manageable (i don't remember how to break a program into multiple files).

Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<iostream>
#include<string>
using namespace std;

struct Semitones{string tone_value;};

enum key {gsharp_aflat, tone_A, asharp_bflat, tone_B, tone_C, csharp_dflat,	
			tone_D, dsharp_eflat, tone_E, tone_F, fsharp_gflat, tone_G};
			
enum intervals {unison, minor2, major2, minor3, major3, perfect4th,
                aug_fourth, perfect5th, minor6, major6, minor7, major7};


int root_finder(Semitones tone[], string input); //function prototype
void print_fretboard(Semitones tone[]);
int set_tuning;

int main()
{
   Semitones note[12];  // resource used by "root_finder" during "scale[]'s" formatting

   Semitones scale[12]; // will contain users scale
                          //- i.e all 12 semitones, but in order according to the key entered by user

    note[gsharp_aflat].tone_value = "G#/Ab";
          note[tone_A].tone_value = "A";
    note[asharp_bflat].tone_value = "A#/Bb";
          note[tone_B].tone_value = "B";
          note[tone_C].tone_value = "C";
    note[csharp_dflat].tone_value = "C#/Db";
          note[tone_D].tone_value = "D";
    note[dsharp_eflat].tone_value = "D#/Eb";
          note[tone_E].tone_value = "E";
          note[tone_F].tone_value = "F";
    note[fsharp_gflat].tone_value = "F#/Gb";
          note[tone_G].tone_value = "G";

    cout<<"Natural minor (Aeolian) Scale Finder\n" << endl;
    cout<<"Enter Key Signature"<<endl;
    string user_input;
    cin>>user_input;
		  
cout<<endl<<endl;
    

 
int root;

root =(root_finder(note, user_input)); //function called will compare the input to the existing "note[]" database and create new scale
											// inside of "scale[]" according to user input 

int n = 0;
do {// stores the intervals corresponding to the detected key, each pass represents one semitone, in ascending order
	scale[n].tone_value = note[(root)].tone_value;
		++n;    // increase both to store the next intervals value into scale
		++root;
	if (root >= 12) // since root may be a value close to 12,
	{root = 0;}         // we need to reset couter to zero so that the values in the array positions
	else                    // less than the original value of root will be properly transferred
	continue;
	} while (n <= 12);
// scale complete
root = 0; // reset root to zero
cout << "     Key Established...\n" 	  << endl;  // the following prints values according to the minor scale,
cout << "Natural minor (Aeolian) scale\n" << endl; // which is defined by 
cout << "     For Root Note ";			  	      // {key, major2, minor3, perfect4, perfect5, minor6, minor7, octave(same as root/key)
cout << scale[unison].tone_value 	<<"\n(";
cout << scale[unison].tone_value  	 <<" ";
cout << scale[major2].tone_value     <<" "; 
cout << scale[minor3].tone_value     <<" ";
cout << scale[perfect4th].tone_value <<" ";
cout << scale[perfect5th].tone_value <<" ";
cout << scale[minor6].tone_value 	 <<" ";
cout << scale[minor7].tone_value	 <<" ";
cout << scale[unison].tone_value 	<<")\n";
cout << endl;

}

int root_finder(Semitones tone[], string input){   //compares users input to values inside the template array "note[]"
	 if (input == tone[0].tone_value)                    //and returns a corresponding array position based on user input
		return(gsharp_aflat);
else if (input == tone[1].tone_value)
		 return(tone_A);
else if (input == tone[2].tone_value)
		return(asharp_bflat);
else if (input == tone[3].tone_value)
		return(tone_B);
else if (input == tone[4].tone_value)
		return(tone_C);
else if (input == tone[5].tone_value)
		return(csharp_dflat);
else if (input == tone[6].tone_value)
		return(tone_D);
else if (input == tone[7].tone_value)
		return(dsharp_eflat);
else if (input == tone[8].tone_value)
		return(tone_E);
else if (input == tone[9].tone_value)
		return(tone_F);
else if (input == tone[10].tone_value)
		return(fsharp_gflat);
else if (input == tone[11].tone_value)
		return(tone_G);
	else
		cout<<"there was an error";
        return 0;
	}


Thank you for taking the time to go through this, btw I realize that there are some moderately dificult musical concepts used here, but nothing too complex that someone who doesnt know anything about music couldnt figure out from a google search. THANK YOU!
But there are some bugs/deficiencies with the program...

at a glance the trouble starts when you get an input from the user -so you must anticipate that. e.g these kinds of input: a, a major, amin, asharp etc. :)

- user has to input the note in upper casing, otherwise program wont recognize it.

you can use the locale's toupper: http://www.cplusplus.com/reference/std/locale/toupper/ to convert lowercase inputs of single character/string length 1 into uppercase

- notes that include flats or sharps have to be entered along with the equivalent note, starting with the lower notes #.(ex. G# is same as Ab, user would need to input G#/Ab (G is lower than A, so G is first))

probably a bit tricky, say the input is "g#", first you might want to check for the input length (or size) then convert the first character to uppercase, then check the second character for '#' if this is true you can append '/Ab' (if the first character is G etc)
check out the strings library: http://www.cplusplus.com/reference/string/
Thanks for the help, im having trouble implementing toupper however... heres what i tried:

1
2
3
4
5
6
7
8
  cin>>input;
  string input_fixed;
  locale loc;

  for (size_t i=0; i<input.length(); ++i){
   input_fixed[i] = toupper(input[i],loc);
}
	cout<<input_fixed;



which compiled fine but after entering something it would output some jibberish and then output the "there was an error" part of "root_finder". Im not familiar with how to use toupper, am i doing it wrong?
nvm i found a way that worked

1
2
3
4
5
6
7
8
9
10
const int max_input = 5;
char user_input[max_input], temp[max_input];

cin.getline(user_input, max_input);								    

int i = 0;
while (user_input[i])
{temp[i] = toupper(user_input[i]); ++i;}							

strcpy(user_input, temp);	
Last edited on
1. Of course the easier way would be just to create a menu like this for example:

Natural minor (Aeolian) Scale Finder
Select Key Signature:
1)G#/Ab 2)A 3)A#/Bb (etc...)

Enter Key Signature (1/2/3...12)
2

You selected key of A, the natural minor scale for A is:
A B C D E F G  

no more input checking worries -just check for integer inputs and use switch-case ;) (sorry for not suggesting this sooner)

2. IMO you can reduce the size of main further -create a function for assigning values (i.e lines 25-36) and another function for lines 53-61 and another for displaying the result (63-75)

3. Also a good time to use what you learn about classes

4. Why only restrict to natural minor? Why not expand it to output all modes?
e.g

Key A:
Ionian: A B C#/Db D E F#/Gb G#/Ab
Aeolian: A B C D E F G
etc...


5. to display frets you can make use of arrays:
1
2
3
4
5
6
7
8
9
    cout << "e|-";
    for(int i = 0; i < 13; i++)
        cout << i << "-";
    cout << "\nB|-";
    for(int i = 0; i < 13; i++)
        cout << i << "-";
    cout << "\nG|-";
    for(int i = 0; i < 13; i++)
        cout << i << "-";


e|-0-1-2-3-4-5-6-7-8-9-10-11-12-
B|-0-1-2-3-4-5-6-7-8-9-10-11-12-
G|-0-1-2-3-4-5-6-7-8-9-10-11-12-

of course this is simply a rough attempt on my part, to make it more efficient perhaps you can cretate a 2D char array to store the positions etc
Last edited on
Hey thanks for the suggestions, wish i would have checked back here sooner; I finally solved the main issues i was having earlier so that user can type anything and it should work as long as its a valid note. Here is what i did:

1
2
3
4
const int max_input = 5;
char user_input[max_input], temp[max_input];

cin.getline(user_input, max_input);

First i changed the input into being a char array, reason being so that i limit the users input to being only 5 characters long. Five being the max since at most the user would input something like "letter + sharp + / + letter + flat". i figured limiting the input would be a good idea also because some string operations i knew i would have to perform do not have any boundary checks.

1
2
3
4
5
6
7
8
9
10
int i = 0;
do
{temp[i] = toupper(user_input[i]); ++i;
if (user_input[i] == '#' || 'b' || '/') 
{temp[i] = user_input[i];++i;}
else 
break;
}while (i <= static_cast<int>(strlen(user_input)));														
	
strcpy(user_input, temp);


Next I created a do-while loop so that the first letter in the input corresponding to a note would be upper cased but if user input a key signature containing a flat('b') for say, "Ab" the flat in that value would not be capitalized. After the loop finishes the properly formatted char array "temp" overwrites the non-formatted "user_input" and is converted into a string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int root_finder(Semitones tone[], string input){ 
if (input.length() > 1)	
{
for(int j=0; j<12; j++)
{unsigned int found = tone[j].tone_value.find(input);
		
if (found != string::npos) 
{return(j);}
else continue;
}
}
else

for(int i=0; i<12; i++)
{
if(input == tone[i].tone_value)
{return(i);}
else continue;
}
cout<< "There was an error"<<endl;
return 13;
}


Lastly, i reworked root_finder function to identify when the string contained a sharp or flat and compare it to each string in the tone[] array, using the .find() member function(by identify i mean recognize there is more than a single character in the input). If the input did not contain a sharp or flat it would be processed inside the other "for" loop which corresponds to strings only 1 character in length(as non-sharp/flatted notes should be). The reason the loops must be kept separate is because the .find() member function will find for example, an input 'A' and match it with "G#/Ab" despite the absence of "b".


my next step will be to map the scale onto the fret-board, my idea is similar to what you have, but i was thinking about adding a boolean value to the "semitones" struct, and then modifying the loop where i transfer note_values from "notes[]" to "scale[]" to make all the notes that are apart of the scale(like major2, minor3...) to have "true" for their bool values. That way ill have some sort of check system to prevent all frets from being listed and only the ones that correspond to the scale.

My eventual goal is to make a database of scales, so eventually i will be adding more than just the minor scale. Right now im just seeing if i can get one scale printed out how i want, and once im good there ill make it more versatile.
Topic archived. No new replies allowed.