-- 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/コントロールチェンジ
MIDI コントローラーを用いた イコライザーアプリの拡張方法 masacom @masacom
話すこと CoreMIDIにおけるMIDIClientRefとMIDIInputPortRefの基本構成 MIDIPacketListからのCCメッセージの解析と値の取得方法 各CC番号とEQスライダーの動的なマッピング設計 MIDIコントローラー自動認識とCCプリセット SwiftUIと組み合わせたリアルタイムなUI同期
DEMO
アプリとMIDIコントローラーを同期したいけど。。 ?
MIDIとは? MIDI (Musical Instrument Digital Interface) は、1983 年に策定された電子楽器間の 通信プロトコル。音楽データをデジタル信号として転送・共有するための業 界標準規格で、音そのものではなく、演奏指示をやりとりするものです MIDI の物理形状 5ピンのDINコネクタ 形状
アプリケーションとの接続方法 CoreMIDIは主にMIDI 1.0に対応。(MIDI 2.0は部分的なサポート有) アプリケーション側でCoreMIDI の設定をした後にUSB-MIDI や Lightning/USBC 等 経由で物理デバイスと接続をすると、双方向通信が可能となります。 物理デバイス例: MIDIコントローラー 双方向通信 MIDI メッセージ USB-MIDI 等で接続 アプリケーション側: CoreMIDI
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 ソース接続
Control Change (CC) メッセージとは?? CoreMIDIではどう判定されるの??
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の仕様そのもの
~ 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
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)
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)
}
CC番号とEQスライダーのマッピング struct MIDIMapping: Identifiable, Codable, Equatable, Hashable { var id: UUID = UUID() // 一意の識別子 var parameterName: String // パラメーター名 var midiCC: Int // CC番号 (−1は未割当) }
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)"
}
参考ドキュメント 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/コントロールチェンジ
自己紹介 Swift 歴6ヶ月の初学者 AVFoundationやCoreMIDIを使用したイコライザー アプリ開発をはじめたところSwift の楽しさに夢 中。iOSエンジニアを目指し奮闘中⚡ mascom @masacom デモ動画のアプリは現在 TestFlight 中です ご興味がありましたらお声がけください 普段はWEBフロントエンドやってます