Embedded : : Linux/: : ALSA

[ALSA] 5. MIDI & Raw MIDI

Jay.P Morgan 2026. 3. 11. 14:31

       

 

 

  5.  MIDI & Raw MIDI

 

  5.0  실전 임베디드 카오디오 시스템에서의 MIDI

 

실전 임베디드 카 오디오 시스템(Automotive Infotainment System)에서 MIDI는 우리가 흔히 생각하는 '음악 제작'보다는 시스템 제어, 알림음 합성, 그리고 외부 장치와의 인터페이스 목적으로 주로 사용됩니다.

주요 활용 사례는 다음과 같습니다.

 

 

1. 효율적인 알림음(ADW - Acoustic Detection & Warning) 재생

자동차에는 안전벨트 미착용, 후방 주차 센서, 방향 지시등 등 수많은 알림음이 필요합니다.

  • 용량 최적화: 고음질 WAV나 MP3 파일을 수십 개 저장하는 대신, 아주 작은 용량의 MIDI 시퀀스와 샘플 음원(Soundfont)만 저장하여 실시간으로 합성(Synthesis)합니다.
  • 동적 변형: 상황에 따라 알림음의 피치(Pitch)나 템포를 실시간으로 바꿀 수 있습니다. 예를 들어, 장애물이 가까워질수록 비프음의 속도를 빠르게 조절하는 로직을 MIDI 컨트롤러 값(Control Change) 변경만으로 구현할 수 있습니다.

 

2. 하드웨어 컨트롤 및 모니터링 (HMI)

임베디드 시스템 내에서 버튼, 노브(Knob), 슬라이더의 물리적 움직임을 소프트웨어에 전달하는 프로토콜로 MIDI가 쓰이기도 합니다.

  • MCU 간 통신: 메인 CPU와 오디오 전용 DSP(Digital Signal Processor) 사이에서 볼륨 값이나 이퀄라이저(EQ) 설정을 주고받을 때 Raw MIDI 바이트 형식을 빌려 쓰면 매우 가볍고 표준화된 통신이 가능합니다.
  • 진단 도구: 엔지니어들이 차량 오디오 성능을 튜닝할 때, 외부 PC와 연결하여 실시간으로 파라미터를 조정하는 인터페이스로 MIDI over USB/Serial을 사용합니다.

 

3. 외부 악기 및 앱 연동 (CarPlay / Android Auto)

최근 인포테인먼트 시스템은 스마트폰 연동이 필수입니다.

  • 가상 악기 앱: 사용자가 차 안에서 스마트폰의 음악 제작 앱을 실행했을 때, 차량의 터치스크린이나 핸들 버튼으로 앱을 제어할 수 있도록 MIDI over Bluetooth USB MIDI 프로토콜을 지원합니다.
  • 악기 연결: 일부 고사양 시스템에서는 실제 미디 건반을 차량 USB 포트에 연결하여 차내 스피커로 연주할 수 있는 기능을 제공하기도 합니다.

 

 

실전 시스템 아키텍처 (Embedded 구조)

실제 임베디드 환경(커널 드라이버 수준)에서는 다음과 같은 흐름으로 처리됩니다.

  1. Application Layer: Android 기반 인포테인먼트 앱이 ALSA Sequencer를 통해 이벤트를 보냄.
  2. ALSA Layer: snd-virmidi 또는 사용자 정의 Raw MIDI 드라이버가 이벤트를 바이트 스트림으로 변환.
  3. Kernel/Driver Layer: 앞서 보신 my_midi_output_trigger 같은 함수가 UART/I2C/SPI를 통해 DSP로 데이터를 전송.
  4. Hardware (DSP): 전달받은 MIDI 바이트를 해석하여 내장된 웨이브테이블(Wavetable) 음원으로 소리를 출력하거나 필터 값을 변경.

 

 

요약하자면

자동차 임베디드 시스템에서 MIDI는 "매우 적은 리소스로 소리를 정밀하게 제어하고 장치 간 통신을 표준화하기 위한 도구"로 정의할 수 있습니다.

 

 

 

  5.1  Raw MIDI API

 

Raw MIDI는 하드웨어 MIDI 포트를 직접 제어합니다:

 

ALSA Raw MIDI API는 리눅스에서 MIDI 하드웨어에 바이트(byte) 단위로 직접 접근할 수 있게 해주는 가장 낮은 수준의 인터페이스입니다. 타이밍이나 라우팅을 관리하는 'Sequencer API'와 달리, 하드웨어와 데이터를 있는 그대로 주고받을 때 사용합니다.

 

 

1. Raw MIDI vs. Sequencer API

어떤 API를 사용할지 결정하는 것이 중요합니다.

  • Raw MIDI: 하드웨어에 대한 독점적 제어권을 갖습니다. 타이밍 처리를 직접 해야 하며, 주로 시스템 익스클루시브(SysEx) 덤프나 저지연(Low-latency) 하드웨어 통신에 적합합니다.
  • Sequencer API: 여러 앱이 동시에 접근할 수 있고, 이벤트 기반이며 고해상도 타임스탬프를 지원합니다. DAW나 가상 악기 개발에 더 유리합니다.

 

 

2. 기본 작업 흐름

Raw MIDI API를 사용할 때는 보통 다음의 단계를 거칩니다.

  1. Open (열기): snd_rawmidi_open()을 호출하여 입력/출력 스트림 핸들을 얻습니다.
  2. Configure (설정): 필요하다면 snd_rawmidi_params()를 통해 버퍼 크기 등을 설정합니다.
  3. I/O (입출력): snd_rawmidi_read()로 들어오는 데이터를 읽거나, snd_rawmidi_write()로 데이터를 보냅니다.
  4. Drain (비우기): snd_rawmidi_drain()을 사용하여 버퍼에 남은 데이터가 모두 전송될 때까지 대기합니다.
  5. Close (닫기): snd_rawmidi_close()로 리소스를 해제합니다.

 

 

3. 주요 함수 및 플래그

함수 설명
snd_rawmidi_open 장치(예: "hw:1,0")에 대한 연결을 엽니다.
snd_rawmidi_write MIDI 출력 포트에 원시 바이트를 씁니다.
snd_rawmidi_read MIDI 입력 포트에서 원시 바이트를 읽습니다.
snd_rawmidi_drain 출력 버퍼가 빌 때까지 프로세스를 차단(block)합니다.
snd_rawmidi_close 핸들을 닫고 리소스를 반환합니다.

주요 플래그:

  • SND_RAWMIDI_NONBLOCK: 비차단 모드로 장치를 엽니다.
  • SND_RAWMIDI_APPEND: (출력 전용) 여러 앱이 포트를 공유할 수 있게 시도합니다.

 

 

4. 코드 예시 (C 언어)

이 코드를 컴파일하려면 libasound2-dev 패키지가 필요하며, -lasound 옵션으로 링크해야 합니다.

 

 
  #include <alsa/asoundlib.h>
  #include <stdio.h>

  int main() {
        int err;
        snd_rawmidi_t *midi_out = NULL;
        // MIDI Note On 데이터: 채널 1(0x90), 중앙 C(0x3C), 벨로시티 100(0x64)
        unsigned char note_on[] = {0x90, 0x3C, 0x64};

        // "hw:1,0,0" 장치를 출력 전용으로 열기
        if  ((err = snd_rawmidi_open(NULL, &midi_out, "hw:1,0,0", 0)) < 0) {
                fprintf(stderr, "MIDI 포트를 열 수 없습니다: %s\n", snd_strerror(err));
                return 1;
        }

        // 바이트 전송
        snd_rawmidi_write(midi_out, note_on, sizeof(note_on));
        snd_rawmidi_drain(midi_out);

        printf("MIDI Note On 신호를 보냈습니다!\n");

        snd_rawmidi_close(midi_out);
        return 0;
  }
 
 
 
 

5. 장치 식별 방법

터미널에서 다음 명령어를 입력하면 현재 시스템의 Raw MIDI 장치 목록을 확인할 수 있습니다.

 

 
  amidi -l
 
 
결과는 hw:CARD,DEVICE,SUBDEVICE 형식으로 출력됩니다 (예: hw:1,0,0).
 

주의사항

  • "Device or resource busy": 다른 프로그램이나 ALSA Sequencer가 이미 해당 장치를 사용 중일 때 발생합니다. Raw MIDI는 대개 독점 접근이 필요합니다.
  • 권한 문제: 유저가 audio 그룹에 속해 있어야 /dev/snd/midi* 장치에 직접 접근할 수 있습니다.

 

 

  5.2  Code: ALSA Raw MIDI Device

 

아래 코드는 리눅스 커널 드라이버(Kernel-space) 수준에서 ALSA Raw MIDI 장치를 구현하는 전형적인 구조입니다. 앞서 설명드린 사용자 수준(User-space) API가 이 드라이버가 제공하는 기능을 호출하게 됩니다.

각 부분의 핵심 역할과 데이터 흐름을 정리해 드릴게요.

 
  /* MIDI 디바이스 생성 */
  struct snd_rawmidi *rmidi;
  int err = snd_rawmidi_new(card, "MyMIDI", 0,
                                                  1, 1,             /* 1 output, 1 input */
                                                  &rmidi);
  rmidi->private_data = chip;
 
  snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
                                         &my_midi_output_ops);
  snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
                                         &my_midi_input_ops);
 
  /* MIDI ops */
  static const struct snd_rawmidi_ops my_midi_output_ops = {
         .open = my_midi_output_open,
         .close = my_midi_output_close,
         .trigger = my_midi_output_trigger,
  };
 
  static void my_midi_output_trigger(struct snd_rawmidi_substream *substream,
                                                                int up)
  {
         struct my_chip *chip = substream->rmidi->private_data;
 
         if (up) {
                /* MIDI 송신 시작: FIFO에서 읽어 하드웨어로 전송 */
                unsigned char byte;
                while (snd_rawmidi_transmit_peek(substream, &byte, 1) == 1) {
                        if (!my_midi_tx_ready(chip))
                                break;
                        my_midi_write(chip, byte);
                        snd_rawmidi_transmit_ack(substream, 1);
                }
         } else {
                /* MIDI 송신 중지 */
         }
  }
 
  /* MIDI 수신 (IRQ에서 호출) */
  static void my_midi_rx_interrupt(struct my_chip *chip)
  {
         unsigned char byte;
 
         while (my_midi_rx_avail(chip)) {
                byte = my_midi_read(chip);
                snd_rawmidi_receive(chip->midi_input, &byte, 1);
         }
  }
 

 

 

 

1. MIDI 디바이스 생성 및 초기화

 
  struct snd_rawmidi *rmidi;
  int err = snd_rawmidi_new(card, "MyMIDI", 0, 1, 1, &rmidi);
  rmidi->private_data = chip;
 
  • snd_rawmidi_new: 커널에 새로운 MIDI 장치를 등록합니다.
    • 1, 1: 각각 출력(Output) 서브스트림 1개와 입력(Input) 서브스트림 1개를 생성한다는 뜻입니다.
  • private_data: 드라이버 개발자가 정의한 구조체(chip)를 연결합니다. 이후 콜백 함수에서 하드웨어 레지스터에 접근할 때 이 포인터를 사용합니다.

 

 

 

2. 오퍼레이션(Ops) 설정

 

 
  snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &my_midi_output_ops);
 
  • ALSA 코어와 하드웨어 사이의 인터페이스를 정의합니다. 유저가 MIDI 데이터를 보낼 때(write), 커널은 여기에 등록된 trigger 함수를 실행합니다.

 

 

3. 출력 트리거 (my_midi_output_trigger)

이 함수는 데이터 전송의 '심장' 역할을 합니다.

  • up 인자: 1이면 전송 시작, 0이면 중지를 의미합니다.
  • snd_rawmidi_transmit_peek: ALSA 소프트웨어 버퍼에 있는 데이터를 삭제하지 않고 살짝 엿보는(Peek) 함수입니다.
  • my_midi_tx_ready: 하드웨어(UART 등)의 송신 FIFO가 비어있어 데이터를 보낼 준비가 되었는지 확인합니다. (Flow Control)
  • my_midi_write: 실제 하드웨어 레지스터에 1바이트를 씁니다.
  • snd_rawmidi_transmit_ack: 하드웨어 전송이 성공했으므로, ALSA 버퍼에서 해당 바이트를 **실제로 제거(Acknowledge)**합니다.

Tip: 보통 트리거 함수 내에서 루프를 돌며 여러 바이트를 한꺼번에 보내거나, 하드웨어 인터럽트가 발생했을 때 다시 트리거를 호출하여 남은 데이터를 보냅니다.

 

4. 수신 인터럽트 (my_midi_rx_interrupt)

외부 장치(건반 등)로부터 데이터가 들어왔을 때 호출되는 인터럽트 핸들러 내의 로직입니다.

  • my_midi_rx_avail: 하드웨어 수신 버퍼에 읽을 데이터가 있는지 확인합니다.
  • snd_rawmidi_receive: 하드웨어에서 읽은 바이트를 ALSA 코어의 수신 버퍼로 밀어넣습니다. * 이렇게 전달된 데이터는 유저 프로그램이 snd_rawmidi_read()를 호출할 때 전달됩니다.

 

 

데이터 흐름 요약

방향 흐름 핵심 함수
송신 (TX) 유저 앱 → ALSA 버퍼 → Trigger → 하드웨어 peek & ack
수신 (RX) 하드웨어 → Interrupt → ALSA 버퍼 → 유저 앱 receive
 

주의할 점

  1. Spinlock: trigger 함수나 인터럽트 핸들러 내에서는 동기화를 위해 spin_lock을 적절히 사용해야 합니다. (코드에는 생략되어 있음)
  2. Atomic context: 트리거 함수는 원자적 문맥(Atomic context)에서 실행될 수 있으므로, 내부에서 잠자기(msleep 등)를 하면 안 됩니다.

이 드라이버를 실제 하드웨어(예: UART, SPI 등)에 이식하는 과정에서 특정 레지스터 설정이나 인터럽트 처리에 대해 더 궁금한 점이 있으신가요?

 

 

 

  5.3  가상 MIDI (snd-virmidi)

 

snd-virmidi 모듈은 가상 MIDI 포트를 생성하여 ALSA Sequencer와 Raw MIDI 간 브리지 역할을 합니다.

 

snd-virmidi는 물리적인 하드웨어 장치 없이 소프트웨어적으로 MIDI 데이터를 주고받을 수 있게 해주는 ALSA의 가상 MIDI 카드 드라이버입니다.

가장 핵심적인 역할은 "Raw MIDI"와 "ALSA Sequencer"라는 두 세계를 이어주는 다리(Bridge) 역할을 하는 것입니다.

 

 

1. 주요 역할: API 간의 가교

리눅스 MIDI 시스템은 크게 두 가지 인터페이스로 나뉩니다.

  • Raw MIDI: /dev/snd/midiC1D0 처럼 바이트 스트림을 직접 다루는 하드웨어 중심 방식.
  • ALSA Sequencer: 타임스탬프와 포트 기반으로 이벤트를 다루는 고수준 방식 (가상 악기, DAW 등에서 사용).

snd-virmidi 모듈을 로드하면 가상의 MIDI 장치가 생성되는데, 이 장치의 Raw MIDI 포트로 데이터를 보내면 자동으로 Sequencer 포트의 이벤트로 변환되어 출력됩니다. 그 반대 방향도 마찬가지입니다.

 

 

2. 왜 사용하나요? (주요 용도)

  • 구형 프로그램 지원: Raw MIDI 방식만 지원하는 오래된 소프트웨어를 현대적인 ALSA Sequencer 기반의 가상 악기나 DAW에 연결하고 싶을 때 사용합니다.
  • 소프트웨어 간 라우팅: 하드웨어 미디 케이블 없이 한 프로그램의 MIDI 출력을 다른 프로그램의 입력으로 넣고 싶을 때 유용합니다.
  • 개발 및 테스트: 실제 미디 건반이나 장비가 없는 환경에서 MIDI 드라이버 관련 코드를 테스트할 때 필수적입니다.

 

 

3. 사용 방법 (커널 모드 로드)

터미널에서 다음 명령어를 입력하여 가상 미디 장치를 활성화할 수 있습니다.

 
  # 가상 미디 장치 1개 생성
  sudo modprobe snd-virmidi midi_devs=1
 

 

로드 후 amidi -l (Raw MIDI 목록)과 aconnect -lp (Sequencer 목록)를 실행해 보면, 동일한 이름의 가상 장치가 양쪽 목록에 모두 나타나는 것을 확인할 수 있습니다.

 

 
  $ modprobe snd-virmidi
  $ aconnect -l
  client 14: 'Virtual Raw MIDI 0-0' [type=kernel]
        0 'VirMIDI 0-0       '
 

 


4. 요약

  • 정체: 소프트웨어로 구현된 가상 MIDI 하드웨어.
  • 특징: Raw MIDI 인터페이스와 Sequencer 인터페이스 간의 양방향 데이터 변환.
  • 비유: "하드웨어 장치인 척하는 가짜 MIDI 카드".

 

 

  5.3  ALSA Sequencer

 

Sequencer는 MIDI 이벤트의 고수준 라우팅과 스케줄링을 제공합니다. Client/Port 모델로 복잡한 MIDI 패치베이 구성이 가능합니다.

 
  /* /dev/snd/seq 인터페이스
   * - 다중 클라이언트/포트 지원
   * - 이벤트 큐잉, 타임스탬프
   * - 동적 라우팅 (aconnect로 포트 연결)
   * - MIDI 파일 재생 (pmidi, aplaymidi)
   */
 

 

 

ALSA Sequencer는 리눅스 사운드 시스템(ALSA)에서 MIDI 데이터를 다루기 위한 고수준(High-level) 인터페이스입니다.

단순히 바이트를 주고받는 Raw MIDI와 달리, ALSA Sequencer는 "이벤트(Event)"와 "타이밍(Timing)"을 관리하는 일종의 'MIDI 허브' 역할을 합니다.

 

 

1. 주요 특징

  • 이벤트 기반 (Event-based): 데이터를 바이트 스트림이 아닌 Note On, Note Off, Control Change 같은 구조화된 객체(Event)로 처리합니다.
  • 멀티 클라이언트 (Multi-client): 하나의 MIDI 포트에 여러 개의 프로그램이 동시에 연결될 수 있습니다. (Raw MIDI의 독점 제어 한계를 극복)
  • 타이밍과 큐 (Queues): 자체적인 타이머를 가지고 있어, "이 노트를 500ms 뒤에 연주해라"와 같은 스케줄링이 가능합니다.
  • 동적 라우팅: aconnect 같은 도구를 사용해 소프트웨어와 하드웨어, 또는 소프트웨어끼리 MIDI 신호를 자유롭게 연결(Patch)할 수 있습니다.

 

 

2. Raw MIDI vs. Sequencer 차이점

구분 Raw MIDI (snd_rawmidi) ALSA Sequencer (snd_seq)
데이터 형태 원시 바이트 (Raw Bytes) 구조화된 이벤트 (Events)
연결 방식 1:1 전용 연결 (독점) N:N 다중 연결 (공유)
타이밍 관리 사용자가 직접 처리해야 함 커널/라이브러리가 스케줄링 지원
주요 용도 시스템 익스클루시브(SysEx), 드라이버 제작 DAW, 가상 악기, 미디 플레이어
 

3. 핵심 개념

  1. 클라이언트 (Client): Sequencer API를 사용하는 프로그램이나 하드웨어 드라이버입니다.
  2. 포트 (Port): 클라이언트 내에 존재하는 실제 '통로'입니다. (예: MIDI 입력 포트, 출력 포트)
  3. 구독 (Subscription): 한 포트의 출력을 다른 포트의 입력으로 연결하는 행위입니다.
  4. 큐 (Queue): 이벤트가 전송될 시간을 관리하는 타임라인입니다.

 

4. 유용한 터미널 명령어

       포트 목록 확인:

 
  aconnect -lp    # (p: playback, i: input)
 
 

       포트 연결 (라우팅):

 
  aconnect 20:0 128:0    # 20번 장치의 0번 포트를 128번의 0번으로 연결
 
 

       연결 해제:


  aconnect -x
 
 

요약하자면

ALSA Sequencer는 리눅스에서 여러 음악 소프트웨어와 하드웨어를 마치 가상의 패치 케이블로 연결해주는 거대한 미디 라우팅 및 타이밍 센터라고 이해하시면 됩니다.

 

 

 

  5.4  ALSA Sequencer API 예제

 

ALSA Sequencer API를 사용하여 MIDI 이벤트를 주고받는 코드는 Raw MIDI보다 조금 더 복잡하지만, 구조는 매우 체계적입니다. 코드를 실행하기 위해서는 libasound2-dev 패키지가 설치되어 있어야 하며, 컴파일 시 -lasound옵션을 붙여야 합니다.

 

 

1. MIDI 이벤트 전송 (Sender)

이 예제는 가상의 출력 포트를 생성하고, 실행 시 중앙 C(Note On) 신호를 즉시 전송합니다.

 
  #include <alsa/asoundlib.h>
  #include <stdio.h>
  #include <unistd.h>

  int main() {
        snd_seq_t *seq;
        int port;

        // 1. Sequencer 열기
        snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
        snd_seq_set_client_name(seq, "MySender");

        // 2. 출력 포트 생성
        port = snd_seq_create_simple_port(seq, "Output Port",
                                                                        SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
                                                                        SND_SEQ_PORT_TYPE_MIDI_GENERIC);

        // 3. 이벤트 준비
        snd_seq_event_t ev;
        snd_seq_ev_clear(&ev);
        snd_seq_ev_set_source(&ev, port);
        snd_seq_ev_set_subs(&ev);      // 구독 중인 모든 포트로 전송
        snd_seq_ev_set_direct(&ev);    // 즉시 전송 설정

        // Note On 이벤트 설정 (채널 0, 노트 60, 벨로시티 100)
        snd_seq_ev_set_noteon(&ev, 0, 60, 100);

        // 4. 이벤트 전송
        snd_seq_event_output_direct(seq, &ev);
        printf("Note On 전송 완료!\n");

        // 잠시 대기 후 종료 (수신 측에서 확인할 시간 확보)
        sleep(1);
        snd_seq_close(seq);
        return 0;
  }
 
 

2. MIDI 이벤트 수신 (Receiver)

이 예제는 포트를 열고 대기하다가, 들어오는 MIDI 이벤트(Note On/Off)를 화면에 출력합니다.

 
  #include <alsa/asoundlib.h>
  #include <stdio.h>

  int main() {
        snd_seq_t *seq;
        int port;

        // 1. Sequencer 열기 (입력 모드)
        snd_seq_open(&seq, "default", SND_SEQ_OPEN_INPUT, 0);
        snd_seq_set_client_name(seq, "MyReceiver");

        // 2. 입력 포트 생성
        port = snd_seq_create_simple_port(seq, "Input Port",
                                                                        SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
                                                                        SND_SEQ_PORT_TYPE_MIDI_GENERIC);

        printf("MIDI 이벤트를 대기 중입니다... (Ctrl+C로 종료)\n");

        while (1) {
                snd_seq_event_t *ev;
                // 3. 이벤트 수신 (이벤트가 올 때까지 블록됨)
                snd_seq_event_input(seq, &ev);

                // 4. 이벤트 타입 확인
                if (ev->type == SND_SEQ_EVENT_NOTEON) {
                        printf("Note On 수신! - 노트: %d, 벨로시티: %d\n",
                                     ev->data.note.note, ev->data.note.velocity);
                } else if (ev->type == SND_SEQ_EVENT_NOTEOFF) {
                        printf("Note Off 수신! - 노트: %d\n", ev->data.note.note);
                }

                snd_seq_free_event(ev);
        }

        snd_seq_close(seq);
        return 0;
  }
 
 
 

3. 컴파일 및 실행 방법

컴파일

 
  gcc sender.c -o sender -lasound
  gcc receiver.c -o receiver -lasound
 
 

실행 및 연결

Sequencer는 포트를 생성만 한다고 바로 연결되지 않습니다. aconnect 도구를 사용해 두 프로그램을 다리 놓아줘야 합니다.

1. 두 프로그램을 각각 다른 터미널에서 실행합니다.

2. 새 터미널에서 aconnect -lp를 입력하여 포트 번호를 확인합니다. (예: Sender가 128:0, Receiver가 129:0)

3. 두 포트를 연결합니다:

4. Bash


  aconnect 128:0 129:0 # 128번 장치의 0번 포트를 129번의 0번으로 연결

 

5. Sender 터미널을 확인하면 메시지가 전송되고, Receiver 터미널에 노트 정보가 찍히는 것을 볼 수 있습니다.

 

 

코드 포인트 요약

  • snd_seq_ev_set_subs(): 이 함수를 쓰면 특정 목적지 주소를 몰라도 내 포트를 '구독'하고 있는 모든 대상에게 이벤트를 뿌려줍니다. 매우 편리하죠.
  • snd_seq_event_output_direct(): 버퍼링 없이 즉시 커널로 이벤트를 보낼 때 사용합니다.
  • 구조체 접근: MIDI 데이터는 ev->data.note.note 처럼 공용체(Union) 구조를 통해 접근하므로, 이벤트 타입에 맞는 필드를 참조해야 합니다.

 

 

 

  5.5  ALSA Sequencer와 소켓 프로그래밍

 

ALSA Sequencer 소켓(Socket) 프로그래밍은 데이터를 주고받는 방식에서 비슷한 면이 많지만, 그 목적과 작동 원리에서 뚜렷한 차이가 있습니다.

이해하기 쉽게 핵심적인 차이점을 비교해 드릴게요.


1. 개념적 비교

구분 소켓 (Socket) 프로그래밍 ALSA Sequencer
비유 전화기/우편: 주소(IP/Port)를 통해 상대방에게 직접 데이터를 보냄 방송국/믹서: 채널을 맞춘 모든 사람에게 신호를 보내고 연결(Patch)함
주 데이터 모든 종류의 바이너리/텍스트 데이터 MIDI 이벤트 (노트, 컨트롤러 정보 등)
핵심 목적 프로세스 간 또는 네트워크 간 통신 멀티미디어 장치/앱 간의 타이밍 기반 데이터 교환
연결 방식 Point-to-Point (1:1 또는 서버/클라이언트) Subscription (구독 기반 N:N 연결)
Export to Sheets

2. 주요 차이점 상세 설명

1) 주소 체계 (Addressing)

  • 소켓: IP 주소와 포트 번호를 사용하여 목적지를 명확히 지정합니다. 데이터를 보내기 위해 상대방의 위치를 정확히 알아야 합니다.
  • Sequencer: Client ID와 Port ID를 사용합니다. 하지만 소켓과 달리 "구독(Subscription)" 모델을 씁니다. 내가 데이터를 보낼 때 수신자가 누구인지 몰라도, 내 포트를 구독 중인 모든 대상에게 데이터가 전달됩니다.

2) 타이밍 관리 (Timing)

  • 소켓: 데이터가 최대한 빨리 도착하는 것이 목표입니다. 전송 시점의 정확한 타이밍보다는 신뢰성과 속도가 중요합니다.
  • Sequencer: **"언제(Timestamp)"**가 매우 중요합니다. "이 메시지를 1초 뒤에 실행해라"와 같은 스케줄링 기능을 커널 수준에서 지원합니다.

3) 연결의 유연성

  • 소켓: 소스 코드 내에서 연결(Connect/Accept)이 이루어져야 합니다.
  • Sequencer: 프로그램 외부에서 aconnect 같은 도구로 실행 중에 연결을 마음대로 바꿀 수 있습니다. 마치 가상의 오디오 케이블을 뺐다 끼웠다 하는 것과 같습니다.

3. 코드 관점에서의 비유

  • 소켓은 마치 전용 회선을 까는 것과 같습니다.
  • C
     
    connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    write(sockfd, buffer, len);
    
  • ALSA Sequencer 게시판에 글을 남기는 것과 같습니다.
  • C
     
    snd_seq_ev_set_subs(&ev); // 이 게시판(포트)을 보는 사람 다 들어라!
    snd_seq_event_output_direct(seq, &ev);
    

4. 실무 선택 기준

  • 소켓을 써야 할 때: 장치 간 일반적인 데이터 전송, 웹 통신, 파일 전송 등 범용적인 통신이 필요할 때.
  • ALSA Sequencer를 써야 할 때: 리눅스 환경에서 MIDI 악기 연동, 음악 앱 간의 동기화, 정밀한 타이밍 제어가 필요한 오디오 시스템 제어 시.

요약하자면

소켓은 "데이터를 정확한 곳에 배달하기 위한 통로"이고, ALSA Sequencer는 "MIDI 이벤트를 여러 앱이 공유하고 정확한 시간에 실행하기 위한 이벤트 버스"입니다.

 

 

'Embedded : : Linux > : : ALSA' 카테고리의 다른 글

[ALSA] 7. HW Dep (Hardware Dependent)  (0) 2026.03.11
[ALSA] 6. Timer API  (0) 2026.03.11
[ALSA] 4. Control (Mixer) 인터페이스  (0) 2026.03.10
[ALSA] 3. PCM 서브시스템  (0) 2026.03.10
[ALSA] ALSA 아키텍처 개요  (0) 2026.03.09