---
title: Metalでやってみた リアルタイム音響アプリ波形アニメーション設計
tags:  #swift #ios #swiftui #metal #waveform #audio-visualization  
author: [masacom](https://docswell.com/user/masacom)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/PEXQ28L6JX.jpg?width=480
description: ■extension DC 2026 Day2 のLT資料  extension DC 2026 Day2 https://extension-dc.connpass.com/event/391185/  この資料は、音声アプリの UI で波形をリアルタイムに動かすために検証した Metal の設計にフォーカスしています。8 分・約 2,100 万サンプルのトラックを毎フレーム並べられない問題から出発し、min/max の Float 配列と LOD で持つデータ、同じ投影式に同じフレームの入力をそろえる再生線の位置合わせ、頂点を再利用して可視範囲だけ描く更新分離の 3 点を中心に解説しました。それでも残る波形のチラつきをピクセルスナップで抑えるところまで扱います。  ■ GitHub MetalWaveformKit https://github.com/masaconm/MetalWaveformKit  ■ 内容 ・波形データの持ち方：一定サンプル数のバケットごとに振幅の min/max だけを残した [Float] と、ズーム（秒/px）に応じた LOD 選択 ・再生線と波形位置の一致：時刻 → NDC の投影式を全要素で共有し、1 フレームの入力を 1 回だけ読む設計。1 フレーム古い入力によるずれ（drift）の実測例 ・Viewport の不変条件テスト：再生位置と表示幅を変えても投影結果が中央（NDC 0）の許容誤差内に収まることを確認 ・更新の分離：頂点をトラック時刻のまま不変の MTLBuffer に置き、time → NDC の変換は頂点シェーダで行う。更新をトラック読込時／LOD 初回使用時／毎フレームの 3 つに分ける ・毎フレーム再構築とキャッシュ再利用の CPU 時間比較（draw(in:) の中央値） ・ピクセルスナップ：カメラ開始位置を 1 drawable pixel 単位で丸めて移動時の明滅を抑える仕組みと、24 倍拡大での ON/OFF 比較 ・まとめ：データ／投影／更新の 3 つの設計ポイント  ■ 本資料で解説をした技術： ・Swift（解析結果を不変の Sendable な値型として共有） ・Metal（MTLBuffer の再利用、Metal Shading Language による頂点シェーダでの投影） ・Swift Testing（Viewport の不変条件テスト） ・LOD（min/max エンベロープの段階的な結合） ・ピクセルスナップ（drawable pixel 単位の丸め）  ■ 備考：デモアプリケーション全体で使用しているが解説を省略した技術 ・GPU 側の最適化と、ラスタライズ結果の検証 ・音声出力との同期精度と、出力レイテンシの補正 ・AVAudioSourceNode のレンダー内部と、ロックフリー化 ・Swift Concurrency の設計（actor 分離・Sendable 準拠の詳細） ・SwiftUI Canvas / Core Graphics との性能比較 ・4x MSAA、CADisplayLink による表示更新、SwiftUI ラッパー（MetalWaveformKit には含まれるが本資料では扱わない）  ■ 参考ドキュメント  ■ 参考ドキュメント  AVAudioEngine | Apple Developer Documentation https://developer.apple.com/documentation/avfaudio/avaudioengine  AVAudioSourceNode | Apple Developer Documentation https://developer.apple.com/documentation/avfaudio/avaudiosourcenode  AVAudioSession.outputLatency | Apple Developer Documentation https://developer.apple.com/documentation/avfaudio/avaudiosession/outputlatency  Metal | Apple Developer Documentation https://developer.apple.com/documentation/metal  MTKView | Apple Developer Documentation https://developer.apple.com/documentation/metalkit/mtkview  Metal Shading Language Specification https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf  Swift Testing | Apple Developer Documentation https://developer.apple.com/documentation/testing  Sendable | Apple Developer Documentation https://developer.apple.com/documentation/swift/sendable  Concurrency | The Swift Programming Language https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/  Canvas | Apple Developer Documentation https://developer.apple.com/documentation/swiftui/canvas  Optimizing iPhone and iPad apps to support ProMotion displays | Apple Developer Documentation https://developer.apple.com/documentation/quartzcore/optimizing-iphone-and-ipad-apps-to-support-promotion-displays  The Scientist and Engineer&#039;s Guide to Digital Signal Processing (Steven W. Smith) https://www.dspguide.com/
published: September 27, 26
canonical: https://docswell.com/s/masacom/KE17XE-MetalWaveformKit
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/PEXQ28L6JX.jpg)

Metalでやってみた
リアルタイム音響アプリ波形アニメーション設計
masacom
@masacom


# Page. 2

![Page Image](https://bcdn.docswell.com/page/3EK9MKLGED.jpg)

自己紹介
職業：フロントエンドエンジニア
個人開発：Swiftで音響アプリ開発をしています
mascom
@masacom
本日のサンプル：MetalWaveformKit
URL: https://github.com/masaconm/MetalWaveformKit.git
技術検証サンプルの本体となる10 Band EQアプ
リは現在 TestFlight 中です。ご興味がありました
らお声がけください！


# Page. 3

![Page Image](https://bcdn.docswell.com/page/L73WYZ3575.jpg)

SAMPLE：MetalWaveformKit
波形描画ライブラリ ／ サンプルアプリ ／ テスト・日英ガイドのご案内
音響アプリケーション開発で検証した波形描画の設計をSwiftパッケージにまとめました
サンプルアプリは合成データで波形を動かします（音声再生なし）
導入・操作方法・検証条件は、リポジトリの日英ガイドを参照してください
URL: https://github.com/masaconm/MetalWaveformKit.git


# Page. 4

![Page Image](https://bcdn.docswell.com/page/87DK5R4YJG.jpg)

話すこと
UI 波形から音をリアルタイムに操作するために検証したこと
time-domain データを Float 配列で扱う更新単位
時刻から画面位置への投影と、フレーム入力の共有
1 フレームごとの頂点全再構築を避ける更新分離
それでも残る波形の揺れへの対処（ピクセルスナップ）
リアルタイム制御をする波形アプリ設計の 3 つのポイント


# Page. 5

![Page Image](https://bcdn.docswell.com/page/VJPKGWM2E8.jpg)

話さないこと
GPU 側の最適化と、ラスタライズ結果の検証
音声出力との同期精度と、出力レイテンシの補正
AVAudioSourceNode のレンダー内部と、ロックフリー化
Swift Concurrency の設計（actor 分離・Sendable 準拠の詳細）
SwiftUI Canvas / Core Graphics との性能比較


# Page. 6

![Page Image](https://bcdn.docswell.com/page/2EVV689XEQ.jpg)

実装したい機能と課題


# Page. 7

![Page Image](https://bcdn.docswell.com/page/57GLN5ZREL.jpg)

UIで波形のリアルタイム操作を実装する際の課題
音声アプリ開発時に起きた課題
1.再生位置のズレ
再生線と再生位置の不一致・ブレ
２.波形線の揺れ・乱れ
音声再生時に波形がチラついて見える
３.操作時の遅延・もたつき
波形スクラブが音声に追従しない 等
RenderingDemoの波形・操作部（合成データ・音声なし）
Point
データ・投影・更新周期の 3 点で整理が必要


# Page. 8

![Page Image](https://bcdn.docswell.com/page/4EQYQZLYJP.jpg)

データの持ち方で悩む


# Page. 9

![Page Image](https://bcdn.docswell.com/page/KJ4WN3DZ71.jpg)

データの持ち方
😩 悩んだこと
例：8 分(約 2,100 万サンプル) にもなるトラックを
丸ごとフレーム画面へ並べたら読み込みすら困難
検証したこと
min/max の Float 配列と LOD で、必要な細かさだけ持たせる


# Page. 10

![Page Image](https://bcdn.docswell.com/page/LE1Y51ZD7G.jpg)

波形データは min/max エンベロープの [Float]
一定サンプル数のバケットごとに、振幅の最小値と最大値だけを残す
1 バケットを拡大
例：8 サンプル ／ 実装は 32 サンプル/バケット
max 0.9
min ­0.6
0.2
0.9
­0.6
0.3
­0.2
0.4
0.1
[ min₀, max₀, min₁, max₁, … ]
Point
­0.4
2 つの Float
時刻 → index は O(1)。解析結果は不変の Sendable な値型として共有する


# Page. 11

![Page Image](https://bcdn.docswell.com/page/GEWG5898J2.jpg)

ズームに応じて LOD（解像度段階）を選ぶ
同じ停止位置で異なる倍率を表示する場合
RenderingDemo：32.000秒で停止（iPad・合成データ・音声なし）
1x
LOD 2
4x
16.00 秒 を表示
LOD 0
64x
4.00 秒 を表示
LOD 0
0.25 秒 を表示
拡大すると、より細かい LOD へ
Point
隣接バケットの min/max を結合し、秒/px に応じて LOD を選ぶ
※UI波形はPCMのmin/maxを集約した表示であり、アナログ信号の連続波形
そのものではありません。


# Page. 12

![Page Image](https://bcdn.docswell.com/page/47ZLN899J3.jpg)

再生線と波形位置の不具合


# Page. 13

![Page Image](https://bcdn.docswell.com/page/YJ6W8PKDJV.jpg)

再生線と波形位置を合わせる
😩 悩んだこと
同じ式で計算しても再生線のブレやずれが発生
検証したこと
同じ式に、同じフレームの入力をそろえる


# Page. 14

![Page Image](https://bcdn.docswell.com/page/GJ5M9KP8J4.jpg)

同じ投影式でも、1 フレーム古い再生位置ならずれる
発表時のデモ取得例：ズーム4 ／表示時刻 2:19.651
Synced
0.0 px
1-frame stale
6.4 px
※HUDは小数1桁で表示。0.0 pxには、厳密な0と表示上の丸めによる0が含まれます。
Point
drift は 2 つの再生時刻を同じ viewport で投影した x の差（px）
出力レイテンシの測定ではない


# Page. 15

![Page Image](https://bcdn.docswell.com/page/LE3WYZ3GE5.jpg)

同じ投影式 ＋ 同じフレームの入力
描画の入力を1回読み、波形・キュー・拍グリッドで viewport を共有する
公開OSSの再生線は画面中央に固定する
要素ごとに別のタイミングで読む場合
波形：いまの値
再生線：前の値
同じ投影式
表示位置がずれる
１回読んで全要素で共有する場合
今フレームの入力
playhead / zoom
波形・キュー
拍グリッド
同じ viewport
同じ time → x
xNDC = ((t ­ visibleStartSec) / viewportSec)
2­1


# Page. 16

![Page Image](https://bcdn.docswell.com/page/8EDK5R4N7G.jpg)

不変条件をViewportでテストする
再生位置と表示時間幅を変え、同じ入力からカメラを作り、
投影結果が中央（NDC 0）の許容誤差内に収まることを確認
※0は浮動小数点でも正確に表せます。ただし、演算結果には丸め誤差が生じ得ます。
※本CPUテストはスナップ適用前の |xNDC| &lt; 10⁻⁵（NDC単位）を確認します。


# Page. 17

![Page Image](https://bcdn.docswell.com/page/V7PKGWMNJ8.jpg)

readFrame はオーディオレンダー側の読み出し位置
発表時の音声付きデモの実装例（音声再生はOSSの対象外）
描画が参照する再生位置
SourceNode
PCM を生成
readFrame
次に読む位置
毎フレーム pull
位置を投影
currentTimeSec = readFrame / sampleRate
音が実際に出るまで（出力レイテンシ）
生成済み PCM
エンジン・出力経路
注) outputLatency と表示遅延の補正は、本トークでは対象外とする
耳へ届く音


# Page. 18

![Page Image](https://bcdn.docswell.com/page/2JVV689YJQ.jpg)

フレームに読み込む頂点の考え方


# Page. 19

![Page Image](https://bcdn.docswell.com/page/5EGLN5ZWJL.jpg)

毎フレーム読み込む頂点数が多すぎる問題
😩 悩んだこと
全頂点の毎フレーム再構築でCPU負荷が増え、
操作に使える時間が減る
検証したこと
頂点をトラック時刻で保持し、更新を 3 つのタイミングに分ける


# Page. 20

![Page Image](https://bcdn.docswell.com/page/4JQYQZLQ7P.jpg)

全頂点の毎フレーム再構築は CPU 時間を消費する
保存済み CSV例：2〜26 秒・各 47 点 ／ draw(in:) の CPU 経過時間の中央値
毎フレーム再構築
キャッシュ再利用
5.336 ms
0.060 ms
HUD の取得例（上の中央値とは別）
発表時の検証デモでの測定例です。OSS配布版での再計測値ではありません。
Point
fps 中央値は両方 60.0。フレーム予算内の CPU 余裕が増えた


# Page. 21

![Page Image](https://bcdn.docswell.com/page/K74WN3DYE1.jpg)

頂点座標はトラック時刻(秒)のまま GPU に置く
不変の MTLBuffer を再利用し、time → NDC の変換は頂点シェーダで行う
Xcode / Shaders.metal
(timeSec, amplitude)
time → x
画面上の波形
保持した頂点
viewport で投影
可視範囲だけ描画


# Page. 22

![Page Image](https://bcdn.docswell.com/page/LJ1Y51ZNEG.jpg)

更新を 3 つのタイミングに分ける
毎フレームの全頂点生成を、LOD ごとの初回構築（キャッシュ）へ移す
Xcode / WaveformRenderer.swift
トラック読込時
各 LOD の初回使用時
PCM 解析 → min/max → LOD
MTLBuffer を構築・保存
Point
毎フレーム
入力・uniform・可視範囲
nil のときだけ構築。同じ LOD に戻ると再利用する。


# Page. 23

![Page Image](https://bcdn.docswell.com/page/GJWG586M72.jpg)

それでも残った波形の揺れ・チラつき


# Page. 24

![Page Image](https://bcdn.docswell.com/page/4EZLN8YM73.jpg)

波形の明滅・ちらつきの原因を調査
😩 悩んだこと
位置も負荷も調整済みだが波形が明滅している
波形線内部が動いてる？ちらつきが止まらない！
検証したこと
カメラの開始位置を 1 drawable pixel 刻みへ丸める


# Page. 25

![Page Image](https://bcdn.docswell.com/page/Y76W8PD57V.jpg)

ピクセルスナップで移動時の明滅を抑える
カメラの開始位置を 1 drawable pixel 単位で丸める
スナップなし：ピクセル被覆率が毎フレーム変わる
小数部分が変わる
画素の被覆率が変動し、揺れて見える
frame 0：2.3 px
frame 1：2.7 px
１px 単位：被覆率を保ったまま平行移動
整数ピクセルの平行移動
固定ズームで位相が一定となる
frame 0：2 px
frame 1：3 px
※上記は、1 セル = 1 drawable pixel。幅 1 px の要素を例にした概念図です
※固定ズームでの明滅を抑える。代わりに最大 0.5 px の丸め誤差と段階的なスクロール


# Page. 26

![Page Image](https://bcdn.docswell.com/page/G75M9K3G74.jpg)

ピクセルスナップを確認
入力を 0 px
0.25 px に1秒ごとに切り替え、黄色い枠内の縁を動画で比較
Snap ON は同じ画素を保ち、OFF では縁の明るさが変わり波形線の乱れとなる
RenderingDemoの実描画2
状態を交互表示。比較画面
で24倍に拡大した画像を使
用
位置の丸め誤差は最大0.5描
画ピクセルで、移動は段階的
になります（固定ズームで比
較）


# Page. 27

![Page Image](https://bcdn.docswell.com/page/9J292WZDER.jpg)

まとめ


# Page. 28

![Page Image](https://bcdn.docswell.com/page/DEY4YLRMJM.jpg)

設計のポイントは３つ
1. データ
min/max の不変配列 ＋ LOD
2. 投
影
同じ式 ＋ 同じフレームの入力
3. 更
新
頂点を再利用し、可視範囲だけ描く
Point
波形をMetalで動かすとき、変わるものと変わらないものを分ける設計を
守ることで、UI上での波形・音声のリアルタイム操作UXを向上できる


# Page. 29

![Page Image](https://bcdn.docswell.com/page/VJNYP4D378.jpg)

ご清聴ありがとうございました！


# Page. 30

![Page Image](https://bcdn.docswell.com/page/YE9P3QGPJ3.jpg)

参考ドキュメント
AVAudioEngine
https://developer.apple.com/documentation/avfaudio/
avaudioengine
AVAudioSourceNode
https://developer.apple.com/documentation/avfaudio/
avaudiosourcenode
AVAudioSession.outputLatency
https://developer.apple.com/documentation/avfaudio/
avaudiosession/outputlatency
Metal
https://developer.apple.com/documentation/metal
MTKView
https://developer.apple.com/documentation/metalkit/mtkview
Metal Shading Language Specification
https://developer.apple.com/metal/Metal-Shading-LanguageSpecification.pdf
Swift Testing
https://developer.apple.com/documentation/testing
Sendable
https://developer.apple.com/documentation/swift/sendable
Swift Concurrency（The Swift Programming Language）
https://docs.swift.org/swift-book/documentation/the-swiftprogramming-language/concurrency/
SwiftUI Canvas
https://developer.apple.com/documentation/swiftui/canvas
ProMotion displays
https://developer.apple.com/documentation/quartzcore/optimizingiphone-and-ipad-apps-to-support-promotion-displays
The Scientist and Engineer&#039;s Guide to DSP（S. W. Smith）
https://www.dspguide.com/


