[Android] 스마트폰으로 전자 키보드 인식하기

sangsu.Park·2021년 9월 13일
0

https://github.com/Const4nt0228/Android-MIDI-Recognizer

🐻 Android Studio에서 javax.sound.midi 사용하기

필자는 eclipse에서 먼저 테스트 구동을 하고 Android Studio로 넘어가려고 했으나...
eclipse에서 사용하는 javax.sound.midi는 안드로이드상에서는 사용이 안되고
Android Studio에서 기본 제공하는 MIDI 라이브러리를 사용해야 된다고 한다

🔻 안드로이드 공식 MIDI 라이브러리
https://source.android.com/devices/audio/midi
🔻 공식 라이브러리를 사용한 프로젝트
https://github.com/philburk/android-midisuite

내 수준에서는 코드가 너무 어렵고 원하는 코드만 가져오기에는 클래스끼리 연관성이 높기 때문에 함부로 건드리기 쉽지 않았다.

eclipse에서 사용하던 코드를 사용하기 위해 깃허브에서 안드로이드용 javax.sound.midi 라이브러리를 구해서 사용했다.

🔻 kshoji github
https://github.com/kshoji/javax.sound.midi-for-Android

🔻 Gradle, Manifest 설정 참고
https://github.com/kshoji/javax.sound.midi-for-Android/wiki/Use-USB-MIDI-features

🐻 Manifest

 <uses-feature android:name="android.hardware.usb.host" />

🐻 App 단위 Gradle

repositories {
    maven { url 'https://github.com/kshoji/javax.sound.midi-for-Android/raw/master/javax.sound.midi/repository' }
    maven { url 'https://github.com/kshoji/USB-MIDI-Driver/raw/master/MIDIDriver/snapshots' }
}
implementation 'jp.kshoji:javax-sound-midi:0.0.4:@aar'
implementation 'jp.kshoji:midi-driver:0.1.5:@aar'
//선언부
UsbMidiSystem usbMidiSystem;
//인식 프로세스가 동작될 액티비티에서 초기화가 이루어져야함
usbMidiSystem = new UsbMidiSystem(this);
usbMidiSystem.initialize();

🐻 javax.sound.midi 로 키보드 입력 받아오기

일단 키보드와 스마트폰을 연결해야하므로 usb midi-c타입 케이블이 필요하다.
정확히 말하자면 usb b cable이 필요하다(흔히 굴러다니는 프린터케이블임)


전자피아노가 midi를 호환하는지 먼저 봐야한다.

테스트에서는 Galaxy S10 과 CASIO CDP-S90 키보드를 연결했다.

🔻 eclipse 상에서 동작시킨 코드는 stack overflow를 참조했다.
https://stackoverflow.com/questions/6937760/java-getting-input-from-midi-keyboard

애플리케이션의 동작은 MainActivity에서 midiRecord 클래스를 호출하는 형식이다.
AsyncTask를 이용하여 백그라운드에서 작업하며 onProgressUpdate를 통해 변경된 결과를 반영시킬수 있다.

 public class RecordAudio extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {



            MidiDevice device;
            MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();

            while(started){
                for (int i = 0; i < infos.length; i++) {
                    try {
                        device = MidiSystem.getMidiDevice(infos[i]);
                        System.out.println(infos[i]);
                     
                        List<Transmitter> transmitters = device.getTransmitters();
                 

                        for (int j = 0; j < transmitters.size(); j++) {
                            // create a new receiver
                            transmitters.get(j).setReceiver(
                                    new MidiInputReceiver(device.getDeviceInfo().toString()));
                        }

                        Transmitter trans = device.getTransmitter();
                        trans.setReceiver(new MidiInputReceiver(device.getDeviceInfo().toString()));

                        device.open();
                        System.out.println(device.getDeviceInfo() + " Was Opened");

                        publishProgress();
                    } catch (MidiUnavailableException e) {
                        Log.e("AudioRecord", "Recording Failed");
                    }
                }
            }
            return null;
        }
public class MidiInputReceiver implements Receiver {
        public String name;

        public MidiInputReceiver(String name) {
            this.name = name;
        }

        public void send(MidiMessage msg, long timeStamp) {

            byte[] aMsg = msg.getMessage();
            // take the MidiMessage msg and store it in a byte array

            //변수를 여기서 바꾸어주었으면 좋겠다 이말이죠.
            //aMsg[0] = velocity
            //aMsg[1] = note
            //aMsg[2] = pressed much

            mds1 = (String.valueOf(aMsg[0]));
            mds2 = (String.valueOf(aMsg[1]));
            mds3 = (String.valueOf(aMsg[2]));

            System.out.println(aMsg[0]);
            System.out.println();
        }

        public void close() {
        }
    }

코드를 간단히 설명하자면

midi 장치를 열어서 정보를 읽어들이면

MidiDevice device;
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();

담은 내용을 가지고 MidiInputRecevier 클래스를 호출뒤 transmitter로 전달한다.

 for (int j = 0; j < transmitters.size(); j++) {
                            // create a new receiver
                            transmitters.get(j).setReceiver(
                                    new MidiInputReceiver(device.getDeviceInfo().toString()));
                        }

이때 호출되는 값은 다음과 같다.

byte[] aMsg = msg.getMessage();
//aMsg[0] = velocity : 누름 세기
//aMsg[1] = note : 음계
//aMsg[2] = pressed much : 지속시간

요 세개의 값만 잘 활용한다면 신디사이저 구현도 어렵지 않을듯...

0개의 댓글