【Raspberry Pi Pico】TinyUSBでMIDI受信

恐る恐るRaspberry Pi PicoのSDKにあるTinyUSBをつかってみたら、恐ろしく簡単にMIDI受信できました。

プロジェクトの作成

pico-sdkのtinyusbにMIDI送信のサンプルプログラムmidi_testがあるので、これをベースにMIDI受信用プロジェクトを作成します。 ファイルの構成は以下の通りです。

usb_midi
    ├─ CMakeLists.txt
    ├─ pico_sdk_import.cmake
    ├─ tusb_config.h
    ├─ usb_descriptors.c
    └─ main.c
  1. 適当な場所にフォルダ作成。今回はusb_midiとしました。
  2. pico-sdk/lib/tinyusb/examples/device/midi_test/srcにある3つのファイルをusb_midiフォルダにコピーします。
  3. pico-examples/pico_sdk_import.cmakeをusb_midiフォルダにコピー。
  4. 以下の内容でCMakeLists.txtを作成。
cmake_minimum_required(VERSION 3.12)

# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)

project(usb_midi C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

set(CMAKE_C_FLAGS_DEBUG "-O0 -g")

add_executable(usb_midi
        main.c
        usb_descriptors.c
        )

target_include_directories(usb_midi PRIVATE ${CMAKE_CURRENT_LIST_DIR})

target_link_libraries(usb_midi PRIVATE
    pico_stdlib
    tinyusb_device
    tinyusb_board)
pico_add_extra_outputs(usb_midi)

# Copy uf2 when the build is complete
add_custom_command(
  TARGET usb_midi
  POST_BUILD
  COMMAND copy ${CMAKE_CURRENT_BINARY_DIR}\\usb_midi.uf2 f:\\
)

MIDI受信用コード

コピーしたサンプルプログラムをそのままビルドすると、シーケンサー的にNoteOn/Offが送信されます。これを変更してNoteOn/Offを受信した時にLEDがついたり消えたりするようにしてみました。

#include "bsp/board.h"
#include "tusb.h"

void midi_task(void)
{
  uint8_t n_data;
  uint8_t msg[4];
  while (tud_midi_available())
  {
    if (n_data = tud_midi_read(msg, 3)) 
    {
      switch (0xf0 & msg[0])
      {
        case 0x80:    // NoteOff
          board_led_off();
          break;
        case 0x90:    // NoteOn
          if (msg[2] == 0)  board_led_off();    // velocity == 0 --> NoteOff
          else              board_led_on();
          break;
        default:
          break;
      }
    }
  }
}

int main(void)
{
  board_init();
  tusb_init();

  while (1)
  {
    tud_task(); // tinyusb device task
    midi_task();
  }

  return 0;
}

tud_midi_available()でデータがあればtud_midi_read()でデータを取り出して、あとはメッセージの種類に応じて処理するだけ。