MIDI Chord Helper機能追加版(GOは五度圏や碁盤の「ご」。別に移動式じゃありません)
Revision | da6b0481fe37a91c513d5ad893754301fd60d1b3 (tree) |
---|---|
Time | 2022-03-20 01:41:26 |
Author | k-hatano <kent.ruffle.mgj626@gmai...> |
Commiter | k-hatano |
五度圏ビュー追加、5ボタンマウス対応、sus2入力対応、他
@@ -1,6 +1,6 @@ | ||
1 | 1 | $(eval SRCS := $(shell find src -name "*.java" | tr '\r' ' ')) |
2 | 2 | |
3 | 3 | MidiChordHelper: $(SRCS) |
4 | - javac -encoding utf8 -d bin $^ | |
5 | - jar cvfm MidiChordHelper.jar mani.mf -C bin/ . | |
4 | + javac -encoding utf8 -d classes $^ | |
5 | + jar cvfm MidiChordHelper.jar mani.mf -C classes/ . | |
6 | 6 |
@@ -368,22 +368,21 @@ public class ButtonIcon implements Icon { | ||
368 | 368 | |
369 | 369 | case RIGHT_PANEL_CONTENT_ICON: |
370 | 370 | if (is_selected) { |
371 | - g2.drawOval(0, 0, width, HEIGHT); | |
372 | - for (int r = 0; r < 180; r += 30) { | |
373 | - g2.drawLine((int)(Math.cos(Math.PI * r / 180) * (width / 2) + (width / 2)), | |
374 | - (int)(Math.sin(Math.PI * r / 180) * (width / 2) + (width / 2)), | |
375 | - (int)(-1 * Math.cos(Math.PI * r / 180) * (width / 2) + (width / 2)), | |
376 | - (int)(-1 * Math.sin(Math.PI * r / 180) * (width / 2) + (width / 2))); | |
371 | + for (int r = 0; r < 360; r += 30) { | |
372 | + g2.fillOval((int)Math.round(Math.cos(Math.PI * r / 180) * (width / 2 - 2) + (width / 2) - 2), | |
373 | + (int)Math.round(Math.sin(Math.PI * r / 180) * (width / 2 - 2) + (width / 2) - 2), | |
374 | + 3, | |
375 | + 3); | |
377 | 376 | } |
378 | 377 | } else { |
379 | - g2.drawLine( 0, HEIGHT / 5 + 1, width, HEIGHT / 5 + 1 ); | |
380 | - g2.drawLine( 0, HEIGHT * 2 / 5 + 1, width, HEIGHT * 2 / 5 + 1 ); | |
381 | - g2.drawLine( 0, HEIGHT * 3 / 5 + 1, width, HEIGHT * 3 / 5 + 1 ); | |
382 | - g2.drawLine( 0, HEIGHT * 4 / 5 + 1, width, HEIGHT * 4 / 5 + 1 ); | |
378 | + g2.drawLine( 0, HEIGHT / 5, width, HEIGHT / 5 ); | |
379 | + g2.drawLine( 0, HEIGHT * 2 / 5, width, HEIGHT * 2 / 5 ); | |
380 | + g2.drawLine( 0, HEIGHT * 3 / 5, width, HEIGHT * 3 / 5 ); | |
381 | + g2.drawLine( 0, HEIGHT * 4 / 5, width, HEIGHT * 4 / 5 ); | |
383 | 382 | |
384 | - g2.drawLine( width / 8, HEIGHT / 5 + 1, width / 8, HEIGHT * 4 / 5 + 1 ); | |
385 | - g2.drawLine( width * 4 / 8, HEIGHT / 5 + 1, width * 4 / 8, HEIGHT * 4 / 5 + 1 ); | |
386 | - g2.drawLine( width * 7 / 8, HEIGHT / 5 + 1, width * 7 / 8, HEIGHT * 4 / 5 + 1 ); | |
383 | + g2.drawLine( width / 8, HEIGHT / 5, width / 8, HEIGHT * 4 / 5 ); | |
384 | + g2.drawLine( width * 4 / 8, HEIGHT / 5, width * 4 / 8, HEIGHT * 4 / 5 ); | |
385 | + g2.drawLine( width * 7 / 8, HEIGHT / 5, width * 7 / 8, HEIGHT * 4 / 5 ); | |
387 | 386 | } |
388 | 387 | break; |
389 | 388 | } |
@@ -349,6 +349,7 @@ public class ChordHelperApplet extends JApplet { | ||
349 | 349 | capoSelecter.valueSelecter.addActionListener(e->clearChord()); |
350 | 350 | } |
351 | 351 | }; |
352 | + chordMatrix.chordDiagram = chordDiagram; | |
352 | 353 | keysigLabel = new KeySignatureLabel() {{ |
353 | 354 | addMouseListener(new MouseAdapter() { |
354 | 355 | @Override |
@@ -498,7 +499,15 @@ public class ChordHelperApplet extends JApplet { | ||
498 | 499 | add( Box.createHorizontalStrut(5) ); |
499 | 500 | add( chordMatrix.capoSelecter ); |
500 | 501 | add( Box.createHorizontalStrut(5) ); |
501 | - add( rightPanelContentToggleButton = new JToggleButton(new ButtonIcon(ButtonIcon.RIGHT_PANEL_CONTENT_ICON))); | |
502 | + add( rightPanelContentToggleButton = new JToggleButton(new ButtonIcon(ButtonIcon.RIGHT_PANEL_CONTENT_ICON)){{ | |
503 | + addItemListener(event->{ | |
504 | + if (((JToggleButton)event.getSource()).isSelected()) { | |
505 | + chordDiagram.switchContent(1); | |
506 | + } else { | |
507 | + chordDiagram.switchContent(0); | |
508 | + } | |
509 | + }); | |
510 | + }}); | |
502 | 511 | add( Box.createHorizontalStrut(2) ); |
503 | 512 | }}; |
504 | 513 | keyboardSequencerPanel = new JPanel() {{ |
@@ -1,4 +1,5 @@ | ||
1 | 1 | package camidion.chordhelper.chorddiagram; |
2 | +import java.awt.BorderLayout; | |
2 | 3 | import java.awt.Color; |
3 | 4 | import java.awt.Insets; |
4 | 5 | import java.util.Arrays; |
@@ -17,11 +18,13 @@ import javax.swing.JRadioButton; | ||
17 | 18 | import javax.swing.JScrollBar; |
18 | 19 | import javax.swing.JToggleButton; |
19 | 20 | import javax.swing.SwingConstants; |
21 | +import javax.swing.JComponent; | |
20 | 22 | |
21 | 23 | import camidion.chordhelper.ButtonIcon; |
22 | 24 | import camidion.chordhelper.ChordDisplayLabel; |
23 | 25 | import camidion.chordhelper.music.Chord; |
24 | 26 | import camidion.chordhelper.music.Note; |
27 | +import camidion.chordhelper.circleoffifth.NoteCircle; | |
25 | 28 | |
26 | 29 | /** |
27 | 30 | * ChordDiagram class for MIDI Chord Helper |
@@ -31,6 +34,9 @@ import camidion.chordhelper.music.Note; | ||
31 | 34 | * http://www.yk.rim.or.jp/~kamide/music/chordhelper/ |
32 | 35 | */ |
33 | 36 | public class ChordDiagram extends JPanel { |
37 | + private JPanel originalChordDiagram; | |
38 | + private NoteCircle circleOfFifth; | |
39 | + | |
34 | 40 | /** コードダイアグラムの対象楽器を示す値 */ |
35 | 41 | public static enum Instrument { |
36 | 42 | Ukulele("A,E,C,G"), |
@@ -68,9 +74,12 @@ public class ChordDiagram extends JPanel { | ||
68 | 74 | * @param capoComboBoxModel カポ値選択コンボボックスのデータモデル |
69 | 75 | */ |
70 | 76 | public ChordDiagram(CapoComboBoxModel capoComboBoxModel) { |
77 | + originalChordDiagram = new JPanel(); | |
78 | + | |
71 | 79 | capoSelecterView.valueSelecter.setModel(capoComboBoxModel); |
72 | - setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); | |
73 | - add(new JPanel() { | |
80 | + | |
81 | + originalChordDiagram.setLayout(new BoxLayout(originalChordDiagram, BoxLayout.Y_AXIS)); | |
82 | + originalChordDiagram.add(new JPanel() { | |
74 | 83 | { |
75 | 84 | setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); |
76 | 85 | setOpaque(false); |
@@ -80,8 +89,8 @@ public class ChordDiagram extends JPanel { | ||
80 | 89 | add(capoSelecterView); |
81 | 90 | } |
82 | 91 | }); |
83 | - add(Box.createHorizontalStrut(5)); | |
84 | - add(new JPanel() { | |
92 | + originalChordDiagram.add(Box.createHorizontalStrut(5)); | |
93 | + originalChordDiagram.add(new JPanel() { | |
85 | 94 | { |
86 | 95 | setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); |
87 | 96 | setOpaque(false); |
@@ -105,6 +114,12 @@ public class ChordDiagram extends JPanel { | ||
105 | 114 | add(variationScrollbar); |
106 | 115 | } |
107 | 116 | }); |
117 | + originalChordDiagram.setVisible(true); | |
118 | + add(originalChordDiagram); | |
119 | + | |
120 | + circleOfFifth = new NoteCircle(); | |
121 | + circleOfFifth.setVisible(false); | |
122 | + add(circleOfFifth); | |
108 | 123 | } |
109 | 124 | /** |
110 | 125 | * コードをテキストに記録するボタン |
@@ -204,4 +219,25 @@ public class ChordDiagram extends JPanel { | ||
204 | 219 | public void setTargetInstrument(Instrument instrument) { |
205 | 220 | instButtons.get(Objects.requireNonNull(instrument)).doClick(); |
206 | 221 | } |
222 | + | |
223 | + public final int CHORD_DIAGRAM = 0; | |
224 | + public final int CIRCLE_OF_FIFTH = 1; | |
225 | + | |
226 | + public void switchContent(int which) { | |
227 | + switch (which) { | |
228 | + case CHORD_DIAGRAM: | |
229 | + originalChordDiagram.setVisible(true); | |
230 | + circleOfFifth.setVisible(false); | |
231 | + break; | |
232 | + case CIRCLE_OF_FIFTH: | |
233 | + originalChordDiagram.setVisible(false); | |
234 | + circleOfFifth.setVisible(true); | |
235 | + circleOfFifth.invalidate(); | |
236 | + break; | |
237 | + } | |
238 | + } | |
239 | + | |
240 | + public void note(boolean isNoteOn, int noteNumber) { | |
241 | + this.circleOfFifth.note(isNoteOn, noteNumber); | |
242 | + } | |
207 | 243 | } |
@@ -31,6 +31,7 @@ import javax.swing.JPanel; | ||
31 | 31 | |
32 | 32 | import camidion.chordhelper.ButtonIcon; |
33 | 33 | import camidion.chordhelper.ChordDisplayLabel; |
34 | +import camidion.chordhelper.chorddiagram.ChordDiagram; | |
34 | 35 | import camidion.chordhelper.chorddiagram.CapoComboBoxModel; |
35 | 36 | import camidion.chordhelper.chorddiagram.CapoSelecterView; |
36 | 37 | import camidion.chordhelper.midieditor.SequenceTickIndex; |
@@ -60,6 +61,8 @@ public class ChordMatrix extends JPanel | ||
60 | 61 | public ChordDisplayLabel chordDisplay = new ChordDisplayLabel("Chord Pad", null, this, null); |
61 | 62 | |
62 | 63 | private NoteWeight noteWeightArray[] = new NoteWeight[Note.SEMITONES_PER_OCTAVE]; |
64 | + | |
65 | + public ChordDiagram chordDiagram = null; | |
63 | 66 | /** |
64 | 67 | * 発音中のノート表示をクリアします。 |
65 | 68 | */ |
@@ -76,6 +79,8 @@ public class ChordMatrix extends JPanel | ||
76 | 79 | int diff = (isNoteOn ? 1 : -1); |
77 | 80 | NoteWeight w = noteWeightArray[Note.mod12(noteNumber)]; |
78 | 81 | if( noteNumber < 49 ) w.addBass(diff); else w.add(diff); |
82 | + | |
83 | + chordDiagram.note(isNoteOn, noteNumber); | |
79 | 84 | } |
80 | 85 | |
81 | 86 | /** |
@@ -543,14 +548,14 @@ public class ChordMatrix extends JPanel | ||
543 | 548 | else |
544 | 549 | intervals.add(Chord.Interval.SEVENTH); |
545 | 550 | } |
546 | - else if( e.isShiftDown() ) | |
551 | + else if( e.isShiftDown() || (e.getButton() == 4) ) | |
547 | 552 | intervals.add(Chord.Interval.SIXTH); |
548 | - if( e.isControlDown() ) | |
553 | + if( e.isControlDown() || (e.getButton() == 5) ) | |
549 | 554 | intervals.add(Chord.Interval.NINTH); |
550 | 555 | else |
551 | 556 | intervals.remove(Chord.Interval.NINTH); |
552 | 557 | |
553 | - if( e.isAltDown() ) { | |
558 | + if( e.isAltDown() || (e.getButton() == 2) ) { | |
554 | 559 | if( cl.isSus4 ) { |
555 | 560 | intervals.add(Chord.Interval.MAJOR); // To cancel sus4 |
556 | 561 | intervals.add(Chord.Interval.SHARP5); |
@@ -58,7 +58,8 @@ public class MidiChannelButtonSelecter extends JList<Integer> | ||
58 | 58 | public MyCellRenderer() { |
59 | 59 | setOpaque(true); |
60 | 60 | setHorizontalAlignment(CENTER); |
61 | - setSelectionBackground(Color.yellow); | |
61 | + setForeground(Color.lightGray); | |
62 | + setSelectionForeground(Color.red); | |
62 | 63 | } |
63 | 64 | @Override |
64 | 65 | public Component getListCellRendererComponent( |
@@ -69,7 +70,10 @@ public class MidiChannelButtonSelecter extends JList<Integer> | ||
69 | 70 | this.cellHasFocus = cellHasFocus; |
70 | 71 | setText(value.toString()); |
71 | 72 | if(isSelected) { |
72 | - setBackground(list.getSelectionBackground()); | |
73 | + setBackground( | |
74 | + keyboard != null && keyboard.countKeyOn(index) > 0 ? | |
75 | + Color.pink : list.getBackground() | |
76 | + ); | |
73 | 77 | setForeground(list.getSelectionForeground()); |
74 | 78 | } else { |
75 | 79 | setBackground( |
@@ -202,6 +202,7 @@ public class Chord { | ||
202 | 202 | set(Interval.MINOR); |
203 | 203 | } |
204 | 204 | else if( outParen.matches(".*sus4.*") ) set(Interval.SUS4); |
205 | + else if( outParen.matches(".*sus2.*") ) set(Interval.SUS2); | |
205 | 206 | // |
206 | 207 | // extended |
207 | 208 | if( outParen.matches(".*9.*") ) { |
@@ -0,0 +1,144 @@ | ||
1 | +package camidion.chordhelper.circleoffifth; | |
2 | +import java.awt.Canvas; | |
3 | +import java.awt.Color; | |
4 | +import java.awt.Dimension; | |
5 | +import java.awt.Graphics; | |
6 | +import java.awt.Insets; | |
7 | +import java.awt.BorderLayout; | |
8 | +import java.awt.event.MouseEvent; | |
9 | +import java.awt.event.MouseListener; | |
10 | +import java.util.stream.Collectors; | |
11 | + | |
12 | +import javax.swing.ButtonGroup; | |
13 | +import javax.swing.JComponent; | |
14 | +import javax.swing.JPanel; | |
15 | +import javax.swing.JRadioButton; | |
16 | +import javax.swing.BoxLayout; | |
17 | + | |
18 | +public class NoteCircle extends JPanel { | |
19 | + private NoteCircleComponent component; | |
20 | + int noteWeights[] = new int[12]; | |
21 | + | |
22 | + public NoteCircle() { | |
23 | + super(); | |
24 | + setLayout(new BorderLayout()); | |
25 | + setPreferredSize(new Dimension(140, 200)); | |
26 | + | |
27 | + component = new NoteCircleComponent(); | |
28 | + component.setSize(128, 128); | |
29 | + add(component, BorderLayout.CENTER); | |
30 | + | |
31 | + JPanel radioButtonsPanel = new JPanel(); | |
32 | + radioButtonsPanel.setLayout(new BoxLayout(radioButtonsPanel, BoxLayout.Y_AXIS)); | |
33 | + | |
34 | + JPanel circleTypePanel = new JPanel(); | |
35 | + ButtonGroup circleTypeGroup = new ButtonGroup(); | |
36 | + circleTypePanel.setLayout(new BoxLayout(circleTypePanel, BoxLayout.X_AXIS)); | |
37 | + | |
38 | + JRadioButton fifthsButton = new JRadioButton("Fifths"); | |
39 | + fifthsButton.setSelected(true); | |
40 | + fifthsButton.addActionListener(event -> { | |
41 | + component.setChromatic(false); | |
42 | + }); | |
43 | + circleTypePanel.add(fifthsButton, BorderLayout.WEST); | |
44 | + circleTypeGroup.add(fifthsButton); | |
45 | + | |
46 | + JRadioButton chromaticButton = new JRadioButton("Chromatic"); | |
47 | + chromaticButton.addActionListener(event -> { | |
48 | + component.setChromatic(true); | |
49 | + }); | |
50 | + circleTypePanel.add(chromaticButton, BorderLayout.EAST); | |
51 | + circleTypeGroup.add(chromaticButton); | |
52 | + | |
53 | + radioButtonsPanel.add(circleTypePanel); | |
54 | + | |
55 | + JPanel accidentalNotationPanel = new JPanel(); | |
56 | + ButtonGroup accidentalNotationGroup = new ButtonGroup(); | |
57 | + accidentalNotationPanel.setLayout(new BoxLayout(accidentalNotationPanel, BoxLayout.X_AXIS)); | |
58 | + | |
59 | + JRadioButton sharpButton = new JRadioButton("Sharp"); | |
60 | + sharpButton.setSelected(true); | |
61 | + sharpButton.addActionListener(event -> { | |
62 | + component.setFlatten(false); | |
63 | + }); | |
64 | + accidentalNotationPanel.add(sharpButton, BorderLayout.WEST); | |
65 | + accidentalNotationGroup.add(sharpButton); | |
66 | + | |
67 | + JRadioButton flatButton = new JRadioButton("Flat"); | |
68 | + flatButton.addActionListener(event -> { | |
69 | + component.setFlatten(true); | |
70 | + }); | |
71 | + accidentalNotationPanel.add(flatButton, BorderLayout.EAST); | |
72 | + accidentalNotationGroup.add(flatButton); | |
73 | + | |
74 | + radioButtonsPanel.add(accidentalNotationPanel); | |
75 | + add(radioButtonsPanel, BorderLayout.SOUTH); | |
76 | + } | |
77 | + | |
78 | + public void note(boolean isNoteOn, int noteNumber) { | |
79 | + if (isNoteOn) { | |
80 | + noteWeights[noteNumber % 12]++; | |
81 | + } else { | |
82 | + noteWeights[noteNumber % 12]--; | |
83 | + if (noteWeights[noteNumber % 12] < 0) { | |
84 | + noteWeights[noteNumber % 12] = 0; | |
85 | + } | |
86 | + } | |
87 | + component.repaint(); | |
88 | + } | |
89 | + | |
90 | + class NoteCircleComponent extends JComponent { | |
91 | + private boolean isFlatten = false; | |
92 | + private boolean isChromatic = false; | |
93 | + private String notesLabelsWithSharp[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; | |
94 | + private String notesLabelsWithFlat[] = {"C", "Db", "D", "Eb", "E", "F", "Gb ", "G", "Ab", "A", "Bb", "B"}; | |
95 | + | |
96 | + public void setChromatic(boolean value) { | |
97 | + isChromatic = value; | |
98 | + repaint(); | |
99 | + } | |
100 | + | |
101 | + public void setFlatten(boolean value) { | |
102 | + isFlatten = value; | |
103 | + repaint(); | |
104 | + } | |
105 | + | |
106 | + public void paintComponent(Graphics g) { | |
107 | + // g.clearRect(0, 0, this.getWidth(), this.getHeight()); | |
108 | + | |
109 | + int unit = Math.min(this.getWidth(), this.getHeight()) - 28; | |
110 | + | |
111 | + for (int i = 0; i < 12; i++) { | |
112 | + int noteIndex; | |
113 | + if (isChromatic) { | |
114 | + noteIndex = i; | |
115 | + } else { | |
116 | + noteIndex = (i * 7) % 12; | |
117 | + } | |
118 | + | |
119 | + int x = (int)((Math.sin(i / 12.0 * 2 * Math.PI) / 2) * (unit)) + this.getWidth() / 2; | |
120 | + int y = (int)((- Math.cos(i / 12.0 * 2 * Math.PI) / 2) * (unit)) + this.getHeight() / 2; | |
121 | + | |
122 | + if (noteWeights[noteIndex] > 0) { | |
123 | + g.setColor(Color.black); | |
124 | + g.fillOval(x - 11, y - 11, 22, 22); | |
125 | + | |
126 | + g.setColor(Color.white); | |
127 | + if (isFlatten) { | |
128 | + g.drawString(notesLabelsWithFlat[noteIndex], x - 4, y + 6); | |
129 | + } else { | |
130 | + g.drawString(notesLabelsWithSharp[noteIndex], x - 4, y + 6); | |
131 | + } | |
132 | + } else { | |
133 | + g.setColor(Color.lightGray); | |
134 | + g.drawOval(x - 11, y - 11, 22, 22); | |
135 | + if (isFlatten) { | |
136 | + g.drawString(notesLabelsWithFlat[noteIndex], x - 4, y + 6); | |
137 | + } else { | |
138 | + g.drawString(notesLabelsWithSharp[noteIndex], x - 4, y + 6); | |
139 | + } | |
140 | + } | |
141 | + } | |
142 | + } | |
143 | + } | |
144 | +} |
@@ -9,6 +9,7 @@ import javax.swing.AbstractAction; | ||
9 | 9 | import javax.swing.Box; |
10 | 10 | import javax.swing.BoxLayout; |
11 | 11 | import javax.swing.JButton; |
12 | +import javax.swing.JCheckBox; | |
12 | 13 | import javax.swing.JPanel; |
13 | 14 | |
14 | 15 | import camidion.chordhelper.ChordDisplayLabel; |
@@ -64,6 +65,9 @@ public class MidiKeyboardPanel extends JPanel { | ||
64 | 65 | add(midiChannelCombobox = new MidiChannelComboSelecter( |
65 | 66 | "MIDI Channel", keyboardCenterPanel.keyboard.midiChComboboxModel |
66 | 67 | )); |
68 | + add(new JCheckBox("Solo"){{ | |
69 | + this.setEnabled(false); | |
70 | + }}); | |
67 | 71 | add(midiChannelButtons = new MidiChannelButtonSelecter( |
68 | 72 | keyboardCenterPanel.keyboard |
69 | 73 | )); |