메이저 스케일을 표시하기 위해 전체를 정리해 보았다.
Major 스케일은 가장 기본적인 스케일로, 3음과 4음 사이, 7음과 8음 사이가 반음이 되도록 음계를 나열한 것이다.
한 옥타브에 음이 12 개가 있으므로 조도 12개가 있을 것으로 기대된다.
하지만 여기에 올림, 내림이 들어가면서 필연적으로 겹치는 조들이 발생한다.
악보 상에서 조표도 다르고 음의 위치도 다르지만 조에 포함된 7음을 연주해보면 결국 똑같은 음이 나오는 조들이 있다.
C# Major(올림 다장조)는 조표에 #이 7개나 붙어있는데, 이는 b이 5 개 붙은 Db Major(내림 라단조)와 완전히 같다. (악보 두 번째와 세 번째)
마찬가지로 D#=Eb, E#=F, E=Fb, F#=Gb,... 뭐 계속 이런 식이다.
대체 어떤 놈 어느 분이 만든거냐?
더 웃기는 것은 bb, ##까지 써야 표시되는 조도 있다는 것이다.
악보를 훑다 보면
D# Major, E#Major, Fb Major, G# Major, A# Major, B# Major에는 조표가 붙어있지 않다.
이런 조들은 그냥 각 음 앞에 임시표를 써붙여서 표시하고 있다.
이런 조에는 쌍샾과 쌍플랫(욕하는 거 같다)의 음들이 들어가 있다. 어짜피 같은 음이 나오는 조가 따로 있으므로 bb와 같은 기호로 악보를 더럽히고 싶지는 않았던 모양이다. 이런 조들은 그냥 사용하지 않는 것 같다.
(경우에 따라 조표에 욱여넣어서 사용하는 경우도 있기는 있는 모양이다)
한편 조표가 있지만 음은 같은 조도 3 케이스가 있다.
C# Major = Db Major
Gb Major = F# Major
B Major = Cb Major
음악 이론이 복잡하고 머리 아픈 이유는 뭔가 심오한 이론 때문이 아니라, 되는대로 막 만들었기 때문이라는 생각이 든다.
일단 여기까지 전체 Major Key를 표시하는 코딩을 시작하기로 했다.
기타는 12 프렛부터 1 프렛을 반복하므로 그림은 12 프렛까지만 하는 것으로 생각했었는데, 나중에 코드톤를 표현하거나 각 블럭이 이어지는 것을 볼 때 조금 불편할 것 같아서 총 14 프렛을 표시하는 것으로 수정하기로 했다.
덕분에 drawFret() 내용이 일부 수정되었고, 메이저 키를 표시하기 위한 degreeMajorArray도 데이터 사이즈가 두 프렛분만큼 늘어나게 되었다.
private final static int [][][] degreeMajorArray = new int[][][] {
// 1 to 6th string
{{4,0,5,0,6,0,7,1,0,2,0,3,4,0},{1,0,2,0,3,4,0,5,0,6,0,7,1,0},{0,6,0,7,1,0,2,0,3,4,0,5,0,6},
{0,3,4,0,5,0,6,0,7,1,0,2,0,3}, {0,7,1,0,2,0,3,4,0,5,0,6,0,7}, {4,0,5,0,6,0,7,1,0,2,0,3,4,0}}, //C
{{3,4,0,5,0,6,0,7,1,0,2,0,3,4},{7,1,0,2,0,3,4,0,5,0,6,0,7,1},{5,0,6,0,7,1,0,2,0,3,4,0,5,0},
{2,0,3,4,0,5,0,6,0,7,1,0,2,0}, {6,0,7,1,0,2,0,3,4,0,5,0,6,0}, {3,4,0,5,0,6,0,7,1,0,2,0,3,4}}, // C#(Db)
{{0,3,4,0,5,0,6,0,7,1,0,2,0,3},{0,7,1,0,2,0,3,4,0,5,0,6,0,7},{0,5,0,6,0,7,1,0,2,0,3,4,0,5},
{0,2,0,3,4,0,5,0,6,0,7,1,0,2}, {0,6,0,7,1,0,2,0,3,4,0,5,0,6}, {0,3,4,0,5,0,6,0,7,1,0,2,0,3}}, // D
{{2,0,3,4,0,5,0,6,0,7,1,0,2,0},{6,0,7,1,0,2,0,3,4,0,5,0,6,0},{4,0,5,0,6,0,7,1,0,2,0,3,4,0},
{1,0,2,0,3,4,0,5,0,6,0,7,1,0}, {5,0,6,0,7,1,0,2,0,3,4,0,5,0}, {2,0,3,4,0,5,0,6,0,7,1,0,2,0}}, // D#(Eb)
{{0,2,0,3,4,0,5,0,6,0,7,1,0,2},{0,6,0,7,1,0,2,0,3,4,0,5,0,6},{3,4,0,5,0,6,0,7,1,0,2,0,3,4},
{7,1,0,2,0,3,4,0,5,0,6,0,7,1}, {0,5,0,6,0,7,1,0,2,0,3,4,0,5}, {0,2,0,3,4,0,5,0,6,0,7,1,0,2}}, // E
{{1,0,2,0,3,4,0,5,0,6,0,7,1,0},{5,0,6,0,7,1,0,2,0,3,4,0,5,0},{0,3,4,0,5,0,6,0,7,1,0,2,0,3},
{0,7,1,0,2,0,3,4,0,5,0,6,0,7}, {4,0,5,0,6,0,7,1,0,2,0,3,4,0}, {1,0,2,0,3,4,0,5,0,6,0,7,1,0}}, // F
{{7,1,0,2,0,3,4,0,5,0,6,0,7,1},{0,5,0,6,0,7,1,0,2,0,3,4,0,5},{2,0,3,4,0,5,0,6,0,7,1,0,2,0},
{6,0,7,1,0,2,0,3,4,0,5,0,6,0}, {3,4,0,5,0,6,0,7,1,0,2,0,3,4}, {7,1,0,2,0,3,4,0,5,0,6,0,7,1}}, // F#(Gb)
{{0,7,1,0,2,0,3,4,0,5,0,6,0,7},{4,0,5,0,6,0,7,1,0,2,0,3,4,0},{0,2,0,3,4,0,5,0,6,0,7,1,0,2},
{0,6,0,7,1,0,2,0,3,4,0,5,0,6}, {0,3,4,0,5,0,6,0,7,1,0,2,0,3}, {0,7,1,0,2,0,3,4,0,5,0,6,0,7}}, // G
{{6,0,7,1,0,2,0,3,4,0,5,0,6,0},{3,4,0,5,0,6,0,7,1,0,2,0,3,4},{1,0,2,0,3,4,0,5,0,6,0,7,1,0},
{5,0,6,0,7,1,0,2,0,3,4,0,5,0}, {2,0,3,4,0,5,0,6,0,7,1,0,2,0}, {6,0,7,1,0,2,0,3,4,0,5,0,6,0}}, // G#(Ab)
{{0,6,0,7,1,0,2,0,3,4,0,5,0,6},{0,3,4,0,5,0,6,0,7,1,0,2,0,3},{7,1,0,2,0,3,4,0,5,0,6,0,7,1},
{0,5,0,6,0,7,1,0,2,0,3,4,0,5}, {0,2,0,3,4,0,5,0,6,0,7,1,0,2}, {0,6,0,7,1,0,2,0,3,4,0,5,0,6}}, // A
{{5,0,6,0,7,1,0,2,0,3,4,0,5,0},{2,0,3,4,0,5,0,6,0,7,1,0,2,0},{0,7,1,0,2,0,3,4,0,5,0,6,0,7},
{4,0,5,0,6,0,7,1,0,2,0,3,4,0}, {1,0,2,0,3,4,0,5,0,6,0,7,1,0}, {5,0,6,0,7,1,0,2,0,3,4,0,5,0}}, // A#(Bb)
{{0,5,0,6,0,7,1,0,2,0,3,4,0,5},{0,2,0,3,4,0,5,0,6,0,7,1,0,2},{6,0,7,1,0,2,0,3,4,0,5,0,6,0},
{3,4,0,5,0,6,0,7,1,0,2,0,3,4}, {7,1,0,2,0,3,4,0,5,0,6,0,7,1}, {0,5,0,6,0,7,1,0,2,0,3,4,0,5}} // B
private void drawFret(Paint paint, Canvas canvas, Bitmap bg, int key)
{
paint.setColor(Color.parseColor(("#101010")));
for(int i=0;i<6;i++)
{
// draw horizontal lines
canvas.drawLine(IMAGE_START_X, IMAGE_START_Y + LINE_DISTANCE_Y * i,
FRET_DISTANCE_X*14+10, IMAGE_START_Y + LINE_DISTANCE_Y * i, paint);
}
paint.setStrokeWidth(4);
// draw rectangle on the Fret0 side
canvas.drawRect(IMAGE_START_X,IMAGE_START_Y,
FRET_START_X,IMAGE_START_Y+LINE_DISTANCE_Y*5,paint);
// draw vertical lines
for(int i=1;i<14;i++)
{
canvas.drawLine(FRET_START_X + FRET_DISTANCE_X * i,IMAGE_START_Y,
FRET_START_X + FRET_DISTANCE_X * i, IMAGE_START_Y + LINE_DISTANCE_Y * 5, paint);
}
canvas.drawLine(FRET_START_X+FRET_DISTANCE_X*11+6,IMAGE_START_Y,
FRET_START_X+FRET_DISTANCE_X*11+6,IMAGE_START_Y+LINE_DISTANCE_Y*5,paint);
// draw triangle Fret mark
Path path = new Path();
paint.setColor(Color.parseColor(("#505050")));
for(int i=0;i<5;i++)
{
drawFretMark(FRET_START_X + i * (FRET_DISTANCE_X * 2), IMAGE_START_Y + LINE_DISTANCE_Y * 5, path);
canvas.drawPath(path, paint);
}
drawFretMark(FRET_START_X+FRET_DISTANCE_X*11+6, IMAGE_START_Y+LINE_DISTANCE_Y*5, path);
canvas.drawPath(path,paint);
paint.setTypeface(Typeface.create(Typeface.DEFAULT_BOLD, Typeface.NORMAL));
paint.setAntiAlias(true);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(38);
for(int fret=0;fret<14;fret++)
for(int line=0;line<6;line++)
if (degreeMajorArray[key][line][fret] != 0){
if (degreeMajorArray[key][line][fret] == 1)
paint.setColor(Color.parseColor(("#F00000")));
else
paint.setColor(Color.parseColor(("#D5D5D5")));
canvas.drawCircle(FRET_START_X + FRET_DISTANCE_X * fret + FRET_DISTANCE_X / 2,
IMAGE_START_Y + LINE_DISTANCE_Y * line , 22, paint);
if (degreeMajorArray[key][line][fret] == 1)
paint.setColor(Color.parseColor(("#F0F0F0")));
else
paint.setColor(Color.parseColor(("#101010")));
String degree = String.valueOf(degreeMajorArray[key][line][fret] );
canvas.drawText(degree,
FRET_START_X + FRET_DISTANCE_X * fret + FRET_DISTANCE_X / 2,
IMAGE_START_Y + LINE_DISTANCE_Y * line + 14, paint);
}
ImageView iv = findViewById(R.id.image);
iv.setImageBitmap(bg);
}
Key 버튼의 동작을 잠시 생각해 보았는데, C에서 B까지의 키 버튼을 누르면 해당 스케일이 표시되고 #,b이 눌리면 그 전에 눌린 키 값에 #과 b가 적용되는 것으로 했다.
버튼이 눌리면 스케일을 표시하는 것과 함께, 상단 text에 현재의 키 스케일을 표시하는 것이 좋을 것 같다.
(코딩 중...)
각 키의 처리는 대충 아래와 같은 식으로 처리하였다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
paint = new Paint();
bg = Bitmap.createBitmap(1650,400, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bg);
currentKey = 0;
drawFret(paint, canvas, bg, currentKey);
setTextCurrentKey(currentKey);
Button buttonC = (Button) findViewById(R.id.button_c) ;
buttonC.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
currentKey = 0;
drawFret(paint, canvas, bg, currentKey);
setTextCurrentKey(currentKey);
}
}) ;
...
Button buttonSharp = (Button) findViewById(R.id.button_sharp) ;
buttonSharp.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
if(currentKey == 11)
currentKey = 0;
else
currentKey += 1;
drawFret(paint, canvas, bg, currentKey);
setTextCurrentKey(currentKey);
}
}) ;
...
}
처리를 위해 Paint, Bitmap, Canvas는 글로벌로 뺏고, 버튼을 그릴 때마다 그림이 겹쳐져서 매번 그리기 전에 화면을 클리어하는 코드를 추가하였다.
현재 키의 text 표시는 아래 함수를 사용하였다.
private void setTextCurrentKey(int key){
TextView txtKey = findViewById(R.id.txt_cur_key);
switch(key)
{
case 0: txtKey.setText("C Major"); break;
case 1: txtKey.setText("C#(Db) Major"); break;
case 2: txtKey.setText("D Major"); break;
case 3: txtKey.setText("D#(Eb) Major"); break;
case 4: txtKey.setText("E Major"); break;
case 5: txtKey.setText("F Major"); break;
case 6: txtKey.setText("F#(Gb) Major"); break;
case 7: txtKey.setText("G Major"); break;
case 8: txtKey.setText("G#(Ab) Major"); break;
case 9: txtKey.setText("A Major"); break;
case 10: txtKey.setText("A#(Bb) Major"); break;
case 11: txtKey.setText("B Major"); break;
}
}
버튼의 텍스트 폰트와 기타 정리를 위해 layout 파일의 내용 일부도 변경되었다.
'Android App 개발 > chordbreaker' 카테고리의 다른 글
나란한 조 (Relative Key) (0) | 2022.11.09 |
---|---|
자연/화성/가락 단음계(Natural/Harmonic/Melodic minor) (0) | 2022.10.30 |
프렛에 도수 표시하기 (0) | 2022.10.12 |
키 버튼의 추가 (0) | 2022.10.12 |
그래픽스 - 지판 그리기 (0) | 2022.10.12 |