수색…


소개

이 주제의 목적은 프로토콜을 조작하는 방법을 보여주는 몇 가지 기본 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