/*
* Copyright (C) 2017 たんらる
*/
package fourthline.mabiicco.ui.editor;
import static org.junit.Assert.*;
import java.awt.Component;
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.List;
import java.util.function.IntConsumer;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
import javax.swing.JButton;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import fourthline.mabiicco.ui.PianoRollView;
import fourthline.UseLoadingDLS;
import fourthline.mabiicco.MabiIccoProperties;
import fourthline.mabiicco.midi.IPlayNote;
import fourthline.mabiicco.ui.IMMLManager;
import fourthline.mmlTools.MMLEventList;
import fourthline.mmlTools.MMLNoteEvent;
import fourthline.mmlTools.MMLScore;
import fourthline.mmlTools.MMLTrack;
import fourthline.mmlTools.core.UndefinedTickException;
public class KeyboardEditorTest extends UseLoadingDLS {
private KeyboardEditor editor;
private IMMLManager mmlManager;
private IPlayNote player;
private IEditAlign editAlign;
private PianoRollView pianoRollView;
private Receiver reciver;
private KeyListener key;
private class StubMmlManager implements IMMLManager {
private MMLScore score;
private StubMmlManager() {
score = new MMLScore();
score.addTrack(new MMLTrack().setMML(""));
}
@Override
public MMLScore getMMLScore() {
return score;
}
@Override
public void setMMLScore(MMLScore score) {}
@Override
public int getActiveTrackIndex() {
return 0;
}
@Override
public MMLEventList getActiveMMLPart() {
return score.getTrack(0).getMMLEventAtIndex(0);
}
@Override
public void updateActivePart(boolean generate) {
try {
score.generateAll();
} catch (UndefinedTickException e) {
e.printStackTrace();
}
}
@Override
public void updateActiveTrackProgram(int trackIndex, int program, int songProgram) {}
@Override
public int getActivePartProgram() {
return 0;
}
@Override
public boolean selectTrackOnExistNote(int note, int tickOffset) {
return false;
}
@Override
public void setMMLselectedTrack(MMLTrack track) {}
@Override
public void addMMLTrack(MMLTrack track) {}
@Override
public void moveTrack(int toIndex) {}
@Override
public void updatePianoRollView() {}
@Override
public void updatePianoRollView(int note) {}
}
private class StubPlayer implements IPlayNote {
@Override
public void playNote(int note, int velocity) {}
@Override
public void playNote(int[] note, int velocity) {}
@Override
public void offNote() {}
}
private class StubAlign implements IEditAlign, IntConsumer {
private int index = 1;
private int align[] = { 192, 48, 24 };
@Override
public int getEditAlign() {
return align[index];
}
@Override
public void accept(int value) {
index = value;
}
}
private boolean originalMidiSetting;
private String originalMidiDeviceSetting;
@Before
public void setup() {
KeyboardEditor.setDebug(true);
originalMidiSetting = MabiIccoProperties.getInstance().midiChordInput.get();
MabiIccoProperties.getInstance().midiChordInput.set(true);
originalMidiDeviceSetting = MabiIccoProperties.getInstance().midiInputDevice.get();
mmlManager = new StubMmlManager();
player = new StubPlayer();
StubAlign align = new StubAlign();
editAlign = align;
pianoRollView = new PianoRollView();
editor = new KeyboardEditor((Frame)null, mmlManager, player, editAlign, pianoRollView);
editor.setNoteAlignChanger(align);
reciver = editor.getReciever();
key = editor.getKeyListener();
}
@After
public void cleanup() {
MabiIccoProperties.getInstance().midiChordInput.set(originalMidiSetting);
MabiIccoProperties.getInstance().midiInputDevice.set(originalMidiDeviceSetting);
KeyboardEditor.setDebug(false);
}
private Component dummyComponent = new JButton();
private void keyTyped(char keyChar) {
key.keyTyped(new KeyEvent(dummyComponent, 0, 0, 0, 0, keyChar));
}
private void keyReleased(char keyChar) {
key.keyReleased(new KeyEvent(dummyComponent, 0, 0, 0, 0, keyChar));
}
private void keyPressed(int keyCode) {
key.keyPressed(new KeyEvent(dummyComponent, 0, 0, 0, keyCode, '?'));
}
private void keyReleased(int keyCode) {
key.keyReleased(new KeyEvent(dummyComponent, 0, 0, 0, keyCode, '?'));
}
private void midiNoteOn(int note) throws InvalidMidiDataException {
reciver.send(new ShortMessage(ShortMessage.NOTE_ON, 0, note, 110), 0);
}
private void midiNoteOff(int note) throws InvalidMidiDataException {
reciver.send(new ShortMessage(ShortMessage.NOTE_ON, 0, note, 0), 0);
}
@Test
public void testCharKeyboard() {
MMLNoteEvent note1 = new MMLNoteEvent(48, 48, 0, 8);
MMLNoteEvent note2 = new MMLNoteEvent(48, 96, 0, 8);
MMLNoteEvent note3 = new MMLNoteEvent(50, 48, 96, 8);
MMLNoteEvent note4 = new MMLNoteEvent(24, 48, 192, 8);
MMLNoteEvent note5 = new MMLNoteEvent(48, 48, 192+48, 8);
List<MMLNoteEvent> eventList = mmlManager.getActiveMMLPart().getMMLNoteEventList();
assertEquals(0, eventList.size());
assertEquals(0, pianoRollView.getSequencePosition());
// 1音目
assertTrue(editor.isEmpty());
keyTyped('c');
assertEquals(1, eventList.size());
assertEquals(note1, eventList.get(0));
assertFalse(editor.isEmpty());
assertEquals(48, pianoRollView.getSequencePosition());
// スペースのばし
keyTyped(' ');
assertEquals(1, eventList.size());
assertEquals(note2, eventList.get(0));
assertFalse(editor.isEmpty());
assertEquals(96, pianoRollView.getSequencePosition());
// キーリリース
keyReleased(' ');
keyTyped('d');
assertEquals(2, eventList.size());
assertEquals(note3, eventList.get(1));
assertFalse(editor.isEmpty());
assertEquals(144, pianoRollView.getSequencePosition());
keyReleased('c');
assertFalse(editor.isEmpty());
keyReleased('d');
assertTrue(editor.isEmpty());
assertEquals(144, pianoRollView.getSequencePosition());
// 休符挿入
keyTyped('r');
assertTrue(editor.isEmpty());
assertEquals(192, pianoRollView.getSequencePosition());
keyReleased('r');
assertTrue(editor.isEmpty());
assertEquals(2, eventList.size());
assertEquals(192, pianoRollView.getSequencePosition());
// オクターブをさげる
keyTyped('<');
keyReleased('<');
keyTyped('<');
keyReleased('<');
assertTrue(editor.isEmpty());
keyTyped('c');
assertEquals(3, eventList.size());
assertEquals(note4, eventList.get(2));
assertFalse(editor.isEmpty());
assertEquals(192+48, pianoRollView.getSequencePosition());
// オクターブをあげる
keyTyped('>');
keyReleased('>');
keyTyped('>');
keyReleased('>');
assertTrue(editor.isEmpty());
keyTyped('c');
assertFalse(editor.isEmpty());
keyReleased('c');
assertEquals(4, eventList.size());
assertEquals(note5, eventList.get(3));
assertTrue(editor.isEmpty());
assertEquals(192+96, pianoRollView.getSequencePosition());
assertEquals("MML@cl8drn24c,,;", mmlManager.getMMLScore().getTrack(0).getMabiMML());
}
@Test
public void testCharKeyboard_SharpFlat() {
MMLTrack expect = new MMLTrack().setMML("MML@l8c+d+g+>c2");
keyTyped('c');
keyTyped('+');
keyReleased('+');
keyReleased('c');
assertTrue(editor.isEmpty());
keyTyped('e');
keyTyped('-');
keyReleased('-');
keyReleased('e');
assertTrue(editor.isEmpty());
keyTyped('g');
keyTyped('#');
keyReleased('#');
keyReleased('g');
assertTrue(editor.isEmpty());
// スペースのばし
keyTyped('b');
keyTyped('+');
keyTyped(' ');
keyReleased(' ');
keyTyped(' ');
keyReleased(' ');
keyTyped(' ');
keyReleased(' ');
keyReleased('+');
keyReleased('b');
assertTrue(editor.isEmpty());
assertEquals(expect.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
}
@Test
public void testCharKeyboard_Velocity_Back() {
MMLTrack expect1 = new MMLTrack().setMML("MML@v10c8v8c8,,;");
MMLTrack expect2 = new MMLTrack().setMML("MML@r8c8,,;");
// 音量Up
keyPressed(KeyEvent.VK_UP);
keyReleased(KeyEvent.VK_UP);
keyPressed(KeyEvent.VK_UP);
keyReleased(KeyEvent.VK_UP);
keyTyped('c');
keyReleased('c');
// 音量Down
keyPressed(KeyEvent.VK_DOWN);
keyReleased(KeyEvent.VK_DOWN);
keyPressed(KeyEvent.VK_DOWN);
keyReleased(KeyEvent.VK_DOWN);
keyTyped('c');
keyReleased('c');
assertTrue(editor.isEmpty());
assertEquals(expect1.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
// 左移動
assertEquals(96, pianoRollView.getSequencePosition());
keyPressed(KeyEvent.VK_LEFT);
keyReleased(KeyEvent.VK_LEFT);
assertEquals(48, pianoRollView.getSequencePosition());
assertTrue(editor.isEmpty());
// バックスペース
keyTyped((char)KeyEvent.VK_BACK_SPACE);
keyReleased((char)KeyEvent.VK_BACK_SPACE);
assertTrue(editor.isEmpty());
assertEquals(expect2.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
// 右移動
assertEquals(0, pianoRollView.getSequencePosition());
keyPressed(KeyEvent.VK_RIGHT);
keyReleased(KeyEvent.VK_RIGHT);
keyPressed(KeyEvent.VK_RIGHT);
keyReleased(KeyEvent.VK_RIGHT);
assertEquals(96, pianoRollView.getSequencePosition());
assertTrue(editor.isEmpty());
// バックスペース
keyTyped((char)KeyEvent.VK_BACK_SPACE);
keyReleased((char)KeyEvent.VK_BACK_SPACE);
assertTrue(editor.isEmpty());
assertEquals("MML@,,;", mmlManager.getMMLScore().getTrack(0).getMabiMML());
}
@Test
public void testCharKeyboard_ChangeNoteAlign() {
MMLTrack expect = new MMLTrack().setMML("MML@c2c16;");
// 1番目のalign指定
keyTyped('1');
keyReleased('1');
keyTyped('c');
keyReleased('c');
// 3番目のalign指定
keyTyped('3');
keyReleased('3');
keyTyped('c');
keyReleased('c');
assertTrue(editor.isEmpty());
assertEquals(expect.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
}
@Test
public void testMidiKeyboard() throws InterruptedException, InvalidMidiDataException {
MMLTrack expect1 = new MMLTrack().setMML("MML@<c8,<e8,<g8;");
MMLTrack expect2 = new MMLTrack().setMML("MML@<c8c,<e8e,<g8g;");
new Thread(() -> editor.setVisible(true)).start();
Thread.sleep(500);
// 和音入力
midiNoteOn(48);
assertFalse(editor.isEmpty());
midiNoteOn(52);
assertFalse(editor.isEmpty());
midiNoteOn(55);
assertFalse(editor.isEmpty());
midiNoteOff(48);
assertFalse(editor.isEmpty());
midiNoteOff(52);
assertFalse(editor.isEmpty());
midiNoteOff(55);
assertTrue(editor.isEmpty());
assertEquals(expect1.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
// 途中のスペース入力
midiNoteOn(48);
assertFalse(editor.isEmpty());
midiNoteOn(52);
assertFalse(editor.isEmpty());
midiNoteOn(55);
assertFalse(editor.isEmpty());
keyTyped(' ');
keyReleased(' ');
midiNoteOff(48);
assertFalse(editor.isEmpty());
midiNoteOff(52);
assertFalse(editor.isEmpty());
midiNoteOff(55);
assertTrue(editor.isEmpty());
assertEquals(expect2.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
editor.setVisible(false);
}
@Test
public void testMidiKeyboard_ChangeEditor() throws InterruptedException, InvalidMidiDataException {
MMLTrack expect = new MMLTrack().setMML("MML@l8<ceg,,;");
new Thread(() -> editor.setVisible(true)).start();
Thread.sleep(500);
editor.changeEditor(false);
// MIDIキーボードの単音入力
midiNoteOn(48);
assertFalse(editor.isEmpty());
midiNoteOn(52);
assertFalse(editor.isEmpty());
midiNoteOn(55);
assertFalse(editor.isEmpty());
midiNoteOff(48);
assertFalse(editor.isEmpty());
midiNoteOff(52);
assertFalse(editor.isEmpty());
midiNoteOff(55);
assertTrue(editor.isEmpty());
assertEquals(expect.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
editor.setVisible(false);
}
@Test
public void testLock() throws InterruptedException, InvalidMidiDataException {
MMLTrack expect = new MMLTrack().setMML("MML@<c8>c8,,;");
new Thread(() -> editor.setVisible(true)).start();
Thread.sleep(500);
// MIDI入力中の文字入力禁止
midiNoteOn(48);
assertFalse(editor.isEmpty());
keyTyped('c');
assertFalse(editor.isEmpty());
keyReleased('c');
assertFalse(editor.isEmpty());
midiNoteOff(48);
assertTrue(editor.isEmpty());
// 文字入力中のMIDI入力禁止
keyTyped('c');
assertFalse(editor.isEmpty());
midiNoteOn(48);
assertFalse(editor.isEmpty());
midiNoteOff(48);
keyReleased('c');
assertTrue(editor.isEmpty());
assertEquals(expect.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
editor.setVisible(false);
}
@Test
public void testLock_changeEditor() throws InterruptedException, InvalidMidiDataException {
MMLTrack expect = new MMLTrack().setMML("MML@<c8c8,,;");
new Thread(() -> editor.setVisible(true)).start();
Thread.sleep(500);
// MIDI入力中のモード切替禁止
midiNoteOn(48);
assertFalse(editor.isEmpty());
editor.changeEditor(false);
assertFalse(editor.isEmpty());
midiNoteOff(48);
assertTrue(editor.isEmpty());
// MIDI入力中のモード切替禁止
midiNoteOn(48);
assertFalse(editor.isEmpty());
editor.changeEditor(true);
assertFalse(editor.isEmpty());
midiNoteOff(48);
assertTrue(editor.isEmpty());
assertEquals(expect.getMabiMML(), mmlManager.getMMLScore().getTrack(0).getMabiMML());
editor.setVisible(false);
}
@Test
public void testRelease() throws InterruptedException, InvalidMidiDataException {
new Thread(() -> editor.setVisible(true)).start();
Thread.sleep(500);
// リリースのみ.
midiNoteOff(48);
assertTrue(editor.isEmpty());
keyReleased('c');
assertTrue(editor.isEmpty());
editor.setVisible(false);
}
}