MIDIコントローラーを用いたiPad用イコライザーアプリの拡張方法

-- Views

September 23, 25

スライド概要

■ iOSDC 2025 のルーキーズLT資料

この資料は、SwiftUIとCoreMIDIを組み合わせた、MIDIコントローラーによるリアルタイム音響制御の実装についてを解説したものです。iOSDC 2025 ルーキーズLT(5分間) に凝縮をした内容となっているため、デモ動画内のサンプルアプリケーション全体の概要については省略をしています。

■iOSDC Japan 2025 プロポーザル
https://fortee.jp/iosdc-japan-2025/proposal/88b5cb87-914e-4fc3-8b2c-2002e8cf9964

■ 内容
・CoreMIDIにおけるMIDIClientRefとMIDIInputPortRefの基本構成
・MIDIPacketListからのCCメッセージの解析と値の取得方法
・各CC番号とEQスライダーの動的なマッピング設計
・MIDIコントローラー自動認識とCCプリセット
・SwiftUIと組み合わせたリアルタイムなUI同期

■ 本資料で解説をした技術:
・Swift
・SwiftUI
・CoreMIDI
・AVFoundation
・リアルタイム音響処理

■ 備考 デモ動画内アプリケーション全体で使用しているが解説を省略した技術
・Metal (MetalWaveform)
・Accelerate
・Combine
・UniformTypeIdentifiers
・AVFAudio


■ 参考ドキュメント

Core MIDI - Apple Developer Documentation
https://developer.apple.com/documentation/coremidi/

Audio Unit Programming Guide
https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioUnitProgrammingGuide

M I D I 1.0 規格書 PDF 一般社団法人 音楽電子事業協会
https://amei.or.jp/midistandardcommittee/MIDI1.0.pdf

MIDI
https://ja.wikipedia.org/wiki/MIDI

コントロールチェンジ
https://ja.wikipedia.org/wiki/コントロールチェンジ

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

MIDI コントローラーを用いた イコライザーアプリの拡張方法 masacom @masacom

2.

話すこと CoreMIDIにおけるMIDIClientRefとMIDIInputPortRefの基本構成 MIDIPacketListからのCCメッセージの解析と値の取得方法 各CC番号とEQスライダーの動的なマッピング設計 MIDIコントローラー自動認識とCCプリセット SwiftUIと組み合わせたリアルタイムなUI同期

3.

DEMO

4.

アプリとMIDIコントローラーを同期したいけど。。 ?

5.

MIDIとは? MIDI (Musical Instrument Digital Interface) は、1983 年に策定された電子楽器間の 通信プロトコル。音楽データをデジタル信号として転送・共有するための業 界標準規格で、音そのものではなく、演奏指示をやりとりするものです MIDI の物理形状 5ピンのDINコネクタ 形状

6.

アプリケーションとの接続方法 CoreMIDIは主にMIDI 1.0に対応。(MIDI 2.0は部分的なサポート有) アプリケーション側でCoreMIDI の設定をした後にUSB-MIDI や Lightning/USBC 等 経由で物理デバイスと接続をすると、双方向通信が可能となります。 物理デバイス例: MIDIコントローラー 双方向通信 MIDI メッセージ USB-MIDI 等で接続 アプリケーション側: CoreMIDI

7.

CoreMIDI での基本構成 MIDIClientRef と MIDIInputPortRef の関係 import CoreMIDI class MIDIManager { var client = MIDIClientRef() // MIDI クライアント var inputPort = MIDIPortRef() //入力ポート /// 外部から MIDI メッセージを受け取るためのハンドラ var midiMessageHandler: (([UInt8]) -> Void)? init() { createMIDIClient() createInputPort() connectAllMIDISources() } // 1. MIDI クライアント作成 // 2. 入力ポート作成 // 3. 全 MIDI ソース接続

8.

Control Change (CC) メッセージとは?? CoreMIDIではどう判定されるの??

9.

Control Change (CC) メッセージの仕様 • ステータスバイト上位 4 ビット = 0xB → Control Change • 下位 4 ビット = 0〜F → チャンネル番号(Ch1〜Ch16 に対応) • この2つを組み合わせてステータスバイト(0xB0〜0xBF)を形成 ステータスバイト Control Change 0xB0 Ch1 0xB1 Ch2 ・・・ ・・・ 0xBF Ch16 Point 0xB0〜0xBF = CCと判定! CoreMIDI フレームワーク(MIDIClientCreate, MIDIPortConnectSource, MIDIReadProc等)は基本的 に MIDI 1.0 Protocol を前提に設計されているもので、判定ロジックはMIDI 1.0の仕様そのもの

10.

~ MIDIPacketList から CC メッセージを解析 Band 周波数 / Frequency 1 32Hz 2 64Hz 3 125Hz 4 250Hz 5 500Hz - 40dB 6 1.0kHz +40dB 7 2.0kHz 8 4.0kHz 9 8.0kHz 10 16.0kHz 調整可能範囲 32Hz CC16

11.
[beta]
MIDIPacketList から CC メッセージを解析
private let midiReadProc: MIDIReadProc = { (packetList, readProcRefCon,
srcConnRefCon) in
let midiManagerInstance =
Unmanaged<MIDIManager>.fromOpaque(readProcRefCon!).takeUnretainedValue()
// パケットからバイト配列を抽出
var midiBytes: [UInt8] = []
// CCメッセージ判定: 0xB0-0xBF
if status & 0xF0 == 0xB0 {
let control = midiMessage[1] // CC番号
let value = midiMessage[2]
以下省略・・・

// 値(0-127)

12.
[beta]
SwiftUIと組み合わせたリアルタイムなUI同期
// 1. CCメッセージ判定

if status & 0xF0 == 0xB0 {
let cc = Int(control)
// 2. マッピング検索
if let mapping = self.midiMappings.first(where:
{ $0.midiCC == cc }) {
// 3. EQパラメーター更新
if mapping.parameterName.hasPrefix("EQ") {
let newGain = Float(Double(value) / 127.0 * 80.0
- 40.0)
// MIDI値 0-127を-40dB〜+40dBに変換

}

}

}

DispatchQueue.main.async {
self.updateEQ(at: index, value: newGain)
}

13.

CC番号とEQスライダーのマッピング struct MIDIMapping: Identifiable, Codable, Equatable, Hashable { var id: UUID = UUID() // 一意の識別子 var parameterName: String // パラメーター名 var midiCC: Int // CC番号 (−1は未割当) }

14.
[beta]
MIDIコントローラー自動認識とCCプリセット
// 1. 接続されたMIDIソース数を取得
let sourceCount = MIDIGetNumberOfSources()
// 2. 各MIDIソースを取得
for i in 0..<sourceCount {
let src = MIDIGetSource(i)

// ← MIDIデバイスを取得

// 3. デバイス名を取得(ここが重要)
var manufacturerRef: Unmanaged<CFString>?
var displayNameRef: Unmanaged<CFString>?
MIDIObjectGetStringProperty(src,
kMIDIPropertyManufacturer, &manufacturerRef)
MIDIObjectGetStringProperty(src,
kMIDIPropertyDisplayName, &displayNameRef)
// 4. 名前を結合
let deviceName = "\(manufacturer) \(displayName)"
}

15.

参考ドキュメント Core MIDI - Apple Developer Documentation https://developer.apple.com/documentation/coremidi/ Audio Unit Programming Guide https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/ AudioUnitProgrammingGuide M I D I 1.0 規格書 PDF 一般社団法人 音楽電子事業協会 https://amei.or.jp/midistandardcommittee/MIDI1.0.pdf MIDI https://ja.wikipedia.org/wiki/MIDI コントロールチェンジ https://ja.wikipedia.org/wiki/コントロールチェンジ

16.

自己紹介 Swift 歴6ヶ月の初学者 AVFoundationやCoreMIDIを使用したイコライザー アプリ開発をはじめたところSwift の楽しさに夢 中。iOSエンジニアを目指し奮闘中⚡ mascom @masacom デモ動画のアプリは現在 TestFlight 中です ご興味がありましたらお声がけください 普段はWEBフロントエンドやってます