arduino
MIDI 통신
수색…
소개
이 주제의 목적은 프로토콜을 조작하는 방법을 보여주는 몇 가지 기본 MIDI 프로그램을 보여주고 복잡한 응용 프로그램에 필요한 유용한 기능을 점진적으로 추가하는 것입니다.
MIDI THRU 예제
MIDI Thru는 간단하고 쉽게 테스트 할 수 있습니다. 제대로 작동하면 Arduino 프로젝트를 두 개의 MIDI 장치, 즉 MIDI IN에서 MIDI OUT으로 설치할 수 있으며 두 장치가 함께 작동하는지 확인할 수 있습니다. 대기 시간을 측정 할 수있는 능력이 있다면 직렬 버퍼 캡처 및 재전송 지침으로 인해 증가 할 것입니다.
// This is a simple MIDI THRU. Everything in, goes right out.
// This has been validate on an Arduino UNO and a Olimex MIDI Shield
boolean byteReady;
unsigned char midiByte;
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
byteReady = false;
midiByte = 0;
}
// The Loop that always gets called...
void loop() {
if (byteReady) {
byteReady = false;
Serial.write(midiByte);
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
midiByte = (unsigned char)Serial.read();
byteReady = true;
}
}
대기열을 통한 MIDI 스루
// This is a more complex MIDI THRU. This version uses a queue. Queues are important because some
// MIDI messages can be interrupted for real time events. If you are generating your own messages,
// you may need to stop your message to let a "real time" message through and then resume your message.
#define QUEUE_DEPTH 128
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// put your setup code here, to run once:
// Set MIDI baud rate:
Serial.begin(31250);
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
MIDI 클럭 생성
// This is a MiDI clk generator. This takes a #defined BPM and
// makes the appropriate clk rate. The queue is used to let other messages
// through, but allows a clock to go immediately to reduce clock jitter
#define QUEUE_DEPTH 128
#define BPM 121
#define MIDI_SYSRT_CLK 0xF8
// clock tracking and calculation
unsigned long lastClock;
unsigned long captClock;
unsigned long clk_period_us;
// Queue Logic for storing messages
int headQ = 0;
int tailQ = 0;
unsigned char tx_queue[QUEUE_DEPTH];
void setup() {
// Set MIDI baud rate:
Serial.begin(31250);
clk_period_us = 60000000 / (24 * BPM);
lastClock = micros();
}
// getQDepth checks for roll over. Folks have told me this
// is not required. Feel free to experiment.
int getQDepth() {
int depth = 0;
if (headQ < tailQ) {
depth = QUEUE_DEPTH - (tailQ - headQ);
} else {
depth = headQ - tailQ;
}
return depth;
}
void addQueue (unsigned char myByte) {
int depth = 0;
depth = getQDepth();
if (depth < (QUEUE_DEPTH-2)) {
tx_queue[headQ] = myByte;
headQ++;
headQ = headQ % QUEUE_DEPTH; // Always keep the headQ limited between 0 and 127
}
}
unsigned char deQueue() {
unsigned char myByte;
myByte = tx_queue[tailQ];
tailQ++;
tailQ = tailQ % QUEUE_DEPTH; // Keep this tailQ contained within a limit
// Now that we dequeed the byte, it must be sent.
return myByte;
}
void loop() {
captClock = micros();
if (lastClock > captClock) {
// we have a roll over condition - Again, maybe we don't need to do this.
if (clk_period_us <= (4294967295 - (lastClock - captClock))) {
// Add a the ideal clock period for this BPM to the last measurement value
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
} else if (clk_period_us <= captClock-lastClock) {
// Basically the same two commands above, but not within a roll over check
lastClock = lastClock + clk_period_us;
// Send a clock, bypasing the transmit queue
Serial.write(MIDI_SYSRT_CLK);
}
if (getQDepth>0) {
Serial.write(deQueue());
}
}
// The little function that gets called each time loop is called.
// This is automated somwhere in the Arduino code.
void serialEvent() {
if (Serial.available()) {
// get the new byte:
addQueue((unsigned char)Serial.read());;
}
}
정의 된 MIDI 메시지
일반적으로 MIDI 프로토콜은 "메시지"로 분류됩니다. 다음과 같은 네 가지 일반 메시지 클래스가 있습니다.
- 채널 음성
- 채널 모드
- 시스템 공통
- 시스템 실시간 메시지
메시지는 0x80 이상의 바이트 값으로 시작합니다. 0x7F 이하의 값은 데이터로 간주됩니다. 효과적으로 127이 하나의 MIDI 데이터 바이트로 인코딩 될 수있는 최대 값이라는 것을 의미합니다. 더 큰 값을 인코딩하려면 두 개 이상의 MIDI 데이터 바이트가 필요합니다.
메시지는 중단없이 시작해야합니다 ... EXCEPT ... 시스템 실시간 메시지. 메시지 중간에 삽입 할 수있는 단일 바이트입니다.
채널 음성 메시지
상태 D7..D0 | 데이터 바이트 | 기술 |
---|---|---|
1000nnnn | 0kkkkkkk 0vvvvvvv | 노트 오프 이벤트.이 메시지는 노트가 릴리스 (종료) 될 때 전송됩니다. (kkkkkkk)는 핵심 (주) 번호입니다. (vvvvvvv)는 속도입니다. |
1001nnnn | 0kkkkkkk 0vvvvvvv | 참고 이벤트. 이 메시지는 노트가 눌려지면 (시작) 보내집니다. (kkkkkkk)는 핵심 (주) 번호입니다. (vvvvvvv)는 속도입니다. |
1010nnnn | 0kkkkkkk 0vvvvvvv | 대위법 키 압력 (애프터 터치). 이 메시지는 "바닥에 쌓인"후 키를 눌러 가장 자주 전송됩니다. (kkkkkkk)는 핵심 (주) 번호입니다. (vvvvvvv)는 압력 값입니다. |
1011nnnn | 0ccccccc 0vvvvvvv | 제어 변경. 이 메시지는 컨트롤러 값이 변경 될 때 전송됩니다. 컨트롤러에는 페달 및 레버와 같은 장치가 포함됩니다. 컨트롤러 번호 120-127은 "채널 모드 메시지"(아래)로 예약되어 있습니다. (ccccccc)는 컨트롤러 번호 (0-119)입니다. (vvvvvvv)는 제어기 값 (0-127)입니다. |
1100nnnn | 0ppppppp | 프로그램 변경. 패치 번호가 변경되면이 메시지가 전송됩니다. (ppppppp)는 새 프로그램 번호입니다. |
1101nnnn | 0vvvvvvv | 채널 압력 (애프터 터치). 이 메시지는 "바닥에 쌓인"후 키를 눌러 가장 자주 전송됩니다. 이 메시지는 Polyphonic After-Touch와 다릅니다. 이 메시지를 사용하여 현재 눌려져있는 모든 키 중 가장 큰 단일 압력 값을 보냅니다. (vvvvvvv)는 압력 값입니다. |
1110nnnn | 0lllllll 0mmmmmmm | 피치 벤드 변경. 이 메시지는 피치 벤더 (일반적으로 휠 또는 레버)가 변경되었음을 나타 내기 위해 전송됩니다. 피치 벤더는 14 비트 값으로 측정됩니다. 센터 (피치 변경 없음)는 2000H입니다. 민감도는 수신기의 함수이지만 RPN 0 (lllllll)을 사용하여 설정할 수 있습니다. lllllll은 최하위 7 비트입니다. (mmmmmmm)은 가장 중요한 7 비트입니다. |
채널 모드 메시지
상태 D7..D0 | 데이터 바이트 | 기술 |
---|---|---|
1011nnnn | 0ccccccc 0vvvvvvv | 채널 모드 메시지. 이는 위의 컨트롤 변경과 동일한 코드이지만 예약 된 컨트롤러 번호 120-127을 사용하여 모드 컨트롤과 특수 메시지를 구현합니다. 명령은 다음과 같습니다. |
모두 소리 끄기. 모든 사운드 끄기가 수신되면 모든 오실레이터는 꺼지고 볼륨 엔벨로프는 가능한 한 빨리 0으로 설정됩니다. c = 120, v = 0 : 모두 소리 끄기 | ||
모든 컨트롤러 재설정. Reset All Controllers를 받으면 모든 컨트롤러 값이 기본값으로 재설정됩니다. (기본값에 대한 구체적인 권장 사례 참조). | ||
c = 121, v = x : 특정 권장 관례에 달리 허용되지 않는 한 값은 0이어야합니다. | ||
로컬 제어. Local Control (로컬 컨트롤)이 Off (꺼짐) 인 경우, 지정된 채널의 모든 장치는 MIDI를 통해 수신 된 데이터에만 응답합니다. 재생 된 데이터 등은 무시됩니다. Local Control On은 일반 컨트롤러의 기능을 복원합니다. | ||
c = 122, v = 0 : 로컬 컨트롤 꺼짐 | ||
c = 122, v = 127 : 로컬 컨트롤 켜짐 | ||
모든 메모 꺼짐. 모든 음표 꺼짐 (All Notes Off)을 수신하면 모든 오실레이터가 꺼집니다. | ||
c = 123, v = 0 : 모든 음표 꺼짐 (실제 모드 명령에 대한 설명은 텍스트 참조) | ||
c = 124, v = 0 : 옴니 모드 꺼짐 | ||
c = 125, v = 0 : 옴니 모드 켜짐 | ||
c = 126, v = M : Mono Mode On (Poly Off) 여기서 M은 채널 수 (Omni Off) 또는 0 (Omni On)입니다. | ||
c = 127, v = 0 : 폴리 모드 켜기 (모노 꺼짐) (참고 :이 네 가지 메시지로 인해 모든 음표가 꺼짐) |
시스템 공통 메시지
상태 D7..D0 | 데이터 바이트 | 기술 |
---|---|---|
11110000 | 0iiiiiii [0iiiiiii 0iiiiiii] 0ddddddd --- --- 0ddddddd 11110111 | 독점적 인 시스템. 이 메시지 유형을 통해 제조업체는 대량 덤프, 패치 매개 변수 및 기타 비 규격 데이터와 같은 자체 메시지를 작성할 수 있으며 추가 MIDI 사양 메시지를 생성하는 메커니즘을 제공합니다. 제조업체의 ID 코드 (MMA 또는 AMEI가 할당)는 1 바이트 (0iiiiiii) 또는 3 바이트 (0iiiiiii 0iiiiiii 0iiiiiii)입니다. 1 Byte ID 중 2 개가 제조업체 고유가 아닌 Universal Exclusive Messages라는 확장 용으로 예약되어 있습니다. 장치가 ID 코드를 자체 메시지 (또는 지원되는 범용 메시지)로 인식하면 메시지의 나머지 부분 (0ddddddd)을 수신합니다. 그렇지 않으면 메시지가 무시됩니다. (참고 : 실시간 메시지 만 시스템 익스 클루 시브로 인터리브 될 수 있습니다.) |
11110001 | 0nnndddd | MIDI 타임 코드 쿼터 프레임. nnn = 메시지 유형 dddd = 값 |
11110010 | 0lllllll 0mmmmmmm | 노래 위치 포인터. 이것은 곡의 시작부터 MIDI 비트의 수 (1 비트 = 6 개의 MIDI 클럭)를 보유하는 내부 14 비트 레지스터입니다. l은 MSB 인 LSB입니다. |
11110011 | 0sssssss | 노래 선택. 곡 선택 (Song Select)은 재생할 시퀀스 또는 곡을 지정합니다. |
11110100 | 정의되지 않음. (예약석) | |
11110101 | 정의되지 않음. (예약석) | |
11110110 | 요청 조정. Tune Request를 수신하면 모든 아날로그 신시사이저가 오실레이터를 튜닝해야합니다. | |
11110111 | 독점의 끝. 시스템 독점 덤프를 종료하는 데 사용됩니다 (위 참조). |
시스템 실시간 메시지
상태 D7..D0 | 데이터 바이트 | 기술 |
---|---|---|
11111000 | 타이밍 시계. 동기화가 필요할 때 사분 음표 당 24 회 전송됩니다 (텍스트 참조). | |
11111001 | 정의되지 않음. (예약석) | |
11111010 | 스타트. 현재 시퀀스 재생을 시작하십시오. (이 메시지는 타이밍 클럭과 함께 나타납니다). | |
11111011 | 잇다. 순서가 중지 된 지점에서 계속하십시오. | |
11111100 | 중지. 현재 시퀀스를 중지하십시오. | |
11111101 | 정의되지 않음. (예약석) | |
11111110 | 능동 감지. 이 메시지는 연결이 살아 있음을 수신자에게 알리기 위해 반복적으로 전송하기위한 것입니다. 이 메시지의 사용은 선택 사항입니다. 처음 수신 될 때, 수신기는 300ms (최대)마다 다른 Active Sensing 메시지를 수신 할 것으로 예상 할 것이며, 그렇지 않은 경우 연결이 종료되었다고 가정합니다. 종료시, 수신기는 모든 음색을 끄고 일반 (비 액티브 감지) 작동으로 돌아갑니다. | |
11111111 | 다시 놓기. 시스템의 모든 수신기를 전원 켜기 상태로 재설정하십시오. 이 방법은 가능한 한 수동으로 제어하는 것이 바람직합니다. 특히 전원을 켤 때 전송해서는 안됩니다. |
Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow