tech
技術記事
-
生成AIと作るリアルタイム音声ビジュアライザー (修正編)
はじめに 前回(生成AIと作るリアルタイム音声ビジュアライザー (実装編))では、
実際にCodexにプロンプトを渡して、実機で動作するアプリを作成出来ましたが、
スペクトラムバーの表示に納得行かない部分が発生しました。 今回の記事ではその原因を元に、対応方法を考えCodexに依頼して修正していきます。 前回まとめた原因 FFT(高速フーリエ変換:Fast Fourier Transform)の出力は、
そのまま使用すると非常に変動が大きく、フレームごとに値が大きく上下します。 マイク入力では、 マイク自体のノイズ 空調音 微小な環境音 などもすべて数値として現れるため、
そのまま表示するとスペクトラムが過敏に反応してしまうことがあります。 多くの実装では次のような処理が行われているので、同様の処理をCodexに依頼して修正していきます。 値の平滑化(Smoothing) 微小ノイズの除去(Noise Floor) 表示用の正規化(Normalization) Codexに安定化を依頼する それではCodexに対して表示の安定化を依頼するプロンプトを作成していきます。 ここで重要なのは、AIに対して「全面的な書き直し」を依頼しないことです。 今回のコードはすでに、 マイク入力の取得 FFT解析 SwiftUIによる表示 といった基本構造は正しく動作しています。 そのため、コードをすべて作り直すのではなく、 平滑化の追加 ノイズの抑制 正規化の改善 といった 局所的な修正のみを依頼する方針にします。 AIに修正を依頼する際は、このように 変更範囲を明確に指定することが重要です。 もし「改善してください」とだけ書くと、AIが アーキテクチャを変更する ファイル構成を変える 不要な抽象化を追加する といった大きな変更を行ってしまうことがあります。 今回はそうした事態を避けるため、既存構造を維持したまま表示を安定化させるように依頼します。 修正を依頼したプロンプト 今回Codexには次のような指示を渡しました。 The app builds and runs correctly, and microphone input is being captured. However, the spectrum bars are too unstable and flicker heavily. Please improve the current implementation by: • increasing smoothing for the spectrum values • adding a simple noise floor threshold to suppress very small fluctuations • improving normalization so the bars are visually more stable • aggregating FFT bins into display bars more smoothly if needed Constraints: • keep the current architecture • do not rewrite the whole project • preserve SwiftUI + AVAudioEngine + Accelerate • keep the implementation simple and educational • show focused code changes only • briefly explain why each change improves visual stability 上記のプロンプトでは、次の点を意識しています。 現在の状態を明確に伝える ビルドは成功している 音声入力は取得できている という状態を明示しています。 これにより、AIが不要な部分を作り直す可能性を減らします。 修正内容を具体的に指定する 今回の問題に対して必要な処理は、主に次の3つです。 平滑化(Smoothing) ノイズ除去(Noise Floor) 正規化(Normalization) この3点を明確に指定することで、AIが意図した方向に修正を行いやすくなります。 変更範囲を制限する 特に重要なのが次の部分です。 keep the current architecture do not rewrite the whole project AIに対してこの制約を入れておかないと、既存コードを大きく変更してしまうことがあります。
今回は 安定化だけを行うことが目的なので、アーキテクチャは維持するように指示しています。 修正後の動作確認 Codexによる修正を適用したあと、再度ビルドと動作確認を行いました。 結果として、ビルドは引き続き成功し、アプリの基本機能も問題なく動作しています。 ビルド結果:BUILD SUCCEEDED アプリ起動:正常 マイク入力:正常に取得 スペクトラム表示:安定化 修正前の状態では、スペクトラムバーがフレームごとに激しく揺れてしまい、
無音に近い状態でも細かい変動が発生していました。 しかし今回の修正によって、 バーの動きが滑らかになった 無音時の微小な揺れがほぼ消えた 音量の変化に対する反応が自然になった といった改善が確認できました。 特に効果が大きかったのは、次の処理です。 上昇・下降で異なる係数を使う時間的平滑化 ノイズフロアによる微小入力の抑制 動的正規化によるスケールの安定化 FFTビンの平均化と空間的平滑化 これらの処理により、
スペクトラム表示はリアルタイム音声ビジュアライザーとして十分に安定した挙動になりました。 初回生成のコードでも基本機能は成立していましたが、
今回の調整によって 実際に見やすいスペクトラム表示 に近づいた形になります。 しかし、自分の環境ではまだ雑音が入っているのか左側のバーが常に動いているため
更に修正を加えます。 追加調整:環境ノイズへの対策 前節の修正により、スペクトラム表示はかなり安定しました。
バーの動きも滑らかになり、無音時の微小な揺れも大幅に減っています。 しかし実際にアプリを動かしてみると、
完全な無音状態でもわずかにバーが反応することが確認できました。 これはバグというより、音声入力を扱うアプリではよくある現象です。 マイク入力には、 マイク自体のノイズ 空調やPCファンなどの環境音 A/D変換時の量子化ノイズ などが含まれるため、完全なゼロ値になることはほとんどありません。 そのため、FFT解析を行うと
人間には聞こえないレベルのノイズでもスペクトラムに現れることがあります。 今回の実装でも、ノイズフロアを導入していましたが、
実機環境ではまだわずかな変動が残っていました。 そこで追加の対策として、 ノイズフロアを少し引き上げる 低レベル入力をより強く抑制する という調整を行いました。 ノイズフロアの調整 修正前は次の値を使用していました。 noiseFloorDb = -62 この値をやや高めに設定することで、環境ノイズの影響をさらに抑えることができます。 例えば次のように調整します。 noiseFloorDb = -25 この変更により、 小さすぎる入力はスペクトラムに表示されない 無音時のバーの揺れがほぼ消える という効果が得られました。 実装で重要なポイント 音声ビジュアライザーでは、解析結果の正確さよりも視覚的な安定性が重要になる場合があります。 そのため多くの実装では、 ノイズフロア 平滑化 正規化 バンド平均化 といった処理を組み合わせて、見やすいスペクトラム表示を作っています。 追加調整:ノイズフロア変更による副作用 前節では環境ノイズの影響を減らすためにノイズフロアを引き上げました。
実機環境でテストした結果、ノイズフロアを -12 dB に設定すると、
ほとんどの環境ノイズを抑えることができました。 この変更により次の改善が確認できました。 無音状態でのバーの揺れがほぼ消える PCファンや空調などの微小ノイズを拾わない スペクトラム表示がより安定する 音声解析では、FFTによって音声を周波数成分に分解しますが、
マイク入力には必ず微小な環境ノイズが含まれます。
これらのノイズは周波数領域でも小さな揺らぎとして現れるため、
可視化では微小なバーの動きとして表示されることがあります。 このような問題を防ぐため、一定以下のレベルを無視する ノイズフロア処理 を導入しました。 発生した問題 しかし、この調整には副作用がありました。 ノイズフロアを高く設定したことで、スペクトラム表示に次の変化が現れました。 バーが全体的に高い位置に集中する 周波数ごとの差が小さくなる スペクトラムの形状が分かりにくくなる つまり、ノイズは抑えられたものの、
スペクトラムのダイナミックレンジが圧縮されてしまった状態です。 今回の実装では、FFT結果をデシベル値へ変換し、
その値を一定の範囲で正規化してバーの高さへ変換しています。 ノイズフロアを大きく変更すると、 最小値が上がる 正規化範囲が狭くなる 全体の値が上側へ寄る という現象が発生します。 その結果、スペクトラムバーが 高い位置でまとまる表示 になってしまいました。 対応方針 この問題を解決するため、次の調整を行うことにしました。 正規化レンジの再調整 ノイズフロア適用後のスケーリング改善 スペクトラム分布を自然にする補正 つまり ノイズは抑える スペクトラムの形状は維持する というバランスを取る形です。 Codexへの修正依頼 この調整はアルゴリズムの細かい部分に関係するため、
既存のコード構造を維持したまま 局所的な修正 をCodexへ依頼しました。 実際に渡したプロンプトは次の通りです。 The app runs correctly and the noise floor adjustment successfully suppresses ambient noise. However, after setting the noise floor to -12 dB, the spectrum bars tend to cluster at high positions and the spectral shape becomes less distinguishable. Please improve the normalization and scaling so that: - the bars use a wider dynamic range - spectral differences between frequency bands remain visible - the spectrum does not collapse into a narrow high-level band Constraints: - keep the current architecture - preserve SwiftUI + AVAudioEngine + Accelerate - do not rewrite the project - adjust normalization or scaling logic only - keep the implementation simple and suitable for educational purposes Briefly explain the reasoning behind the changes. AIとの反復改善 今回のケースは、AIを使った開発の特徴がよく表れている例です。 初回生成では 音声入力 FFT解析 スペクトラム表示 といった基本機能は問題なく実装されていました。 しかし実際にアプリを動かしてみると、 ノイズ抑制 正規化のバランス 視覚的な分かりやすさ といった 体験品質に関わる部分は、細かな調整が必要でした。 そのため今回の開発では AIが土台を生成する
↓
実機で挙動を確認する
↓
問題を整理する
↓
AIに局所的修正を依頼する という反復プロセスで改善を進めています。 まとめ 今回は SwiftUI・AVAudioEngine・Accelerate を使った
リアルタイム音声ビジュアライザーのサンプルアプリを、Codexと協力して作成しました。 開発の流れを振り返ると、次のようなステップで進んでいます。 SwiftUIプロジェクトを作成 マイク権限を設定 Codexに実装を依頼 初回生成コードをビルドして動作確認 スペクトラム表示の不安定さを修正 ノイズフロアを導入して環境ノイズを抑制 正規化やスケーリングを調整して表示品質を改善 初回生成の段階でも、Codexは 音声入力の取得 FFTによる周波数解析 SwiftUIによるスペクトラム表示 といった基本機能を正しく実装していました。 しかし実際にアプリを動かしてみると、 バーの揺れ 環境ノイズの影響 正規化による表示バランス など、動作品質に関する細かな問題が見えてきます。 今回の開発では、 AIが土台を生成する
↓
人間が実機で挙動を確認する
↓
問題点を整理する
↓
AIに局所的な修正を依頼する という 反復的な開発プロセスで改善を進めました。 これは実際の開発でもよく見られるスタイルで、
AIを「自動コード生成ツール」として使うというよりも、 設計や問題分析を人間が行い、実装の補助をAIに任せる という役割分担で作業を進めるものです。 昨今、すべてをAIAgentに任せて最終製品を作れるという記事は増えていますが、
結局の所、最終的な製品の調整は人間が担う事になります。 その時、どういった制限をプロンプトにいれながら進めれば破綻しないのか、
といった部分の理解の一助になれば幸いです。 参考記事 生成AIと作るリアルタイム音声ビジュアライザー (準備編)
生成AIと作るリアルタイム音声ビジュアライザー (実装編) -
生成AIと作るリアルタイム音声ビジュアライザー (実装編)
はじめに 前回(生成AIと作るリアルタイム音声ビジュアライザー (準備編))では、
実装を行うための環境構築を行いました。
この記事では、実際にCodexに渡したプロンプトの説明を行いつつ、
実際に生成されたコードを動かし、修正を行っていきます。 Codexに渡すプロンプト ここからは、実際に Codexへどのような指示を出したか を整理していきます。 今回のようにAIを使用して作成するアプリでは、最初のプロンプト設計がかなり重要です。
理由は、AIに対して曖昧な依頼をすると、 不要に複雑な構成になる UIKitベースの実装が混ざる FFTまわりが雑になる 動いても記事にしづらいコードになる といった問題が起きやすいためです。
そのため今回は、最初から役割と制約を明示したプロンプト を使用します。
おまけ:
実際の開発や継続的なメンテナンスにおいてはAGENTS.mdに主要なルールを記載し、
プロジェクト特有のルールを mdファイルとして配下のディレクトリに設置するなどします。
興味があればハーネスエンジニアリング(Harness Engineering)で調べてみてください。
(この言葉は元々違う分野で一般的に使用される言葉でしたが、
2026年段階で主にAIAgent系の記事で頻出するようになってきています。) 今回のプロンプトで意識したこと 今回Codexに依頼するにあたって、特に意識したポイントは次の通りです。 「まず動くもの」を優先する 最初から理想的な設計を求めるのではなく、
まずは 小さくても動くサンプル を作ることを優先します。 音声処理まわりは、設計をきれいにしようとすると一気に複雑になりやすいため、
初回はあくまで マイク入力が取れる スペクトラムが表示される SwiftUIで動く という最低限の成立を目標にします。 過剰な抽象化を避ける AIは放っておくと、 Protocol Manager Coordinator Utility層 のようなものを増やしがちです。 もちろん実務では有効な場合もありますが、今回の記事の目的は 学習用サンプルを作ること です。 そのため、 ファイル数は少なめ 責務は分かれている ただし過剰設計はしない というバランスを狙います。 SwiftUI / AVAudioEngine / Accelerate を明示する 今回の技術スタックは最初から決めています。 UIは SwiftUI 音声入力は AVAudioEngine 解析は Accelerate / vDSP このあたりを曖昧にすると、AIが別の実装方式を混ぜてくることがあります。 特に音声系では、 AVAudioRecorder AudioKit風の構成 UIKitベースのView 必要以上に複雑なDSP処理 などに寄ってしまう可能性があるため、使う技術は明確に指定しておきます。 記事向けの読みやすさを重視する 今回作りたいのは「製品コード」ではなく、
技術記事の中で解説しやすいサンプルコード です。 そのため、プロンプトの中でも 小さい構成にする 読みやすい命名にする コメントを適度に入れる デバッグしやすい構造にする といった点を明示しています。 実際に渡したプロンプト 以下のような意図を踏まえたうえで、Codexには次のような依頼を行いました。
(プロンプトは英語に翻訳したうえで投入しています) Create a small but working iOS sample app using SwiftUI that visualizes live microphone input in real time. Goal: - Build a technical sample app for a blog article - Keep the code easy to read and easy to debug - Prioritize a simple working implementation over advanced architecture Tech requirements: - SwiftUI for UI - AVAudioEngine for microphone capture - Accelerate / vDSP for FFT or frequency-domain magnitude analysis - iOS 17+ - Swift - No third-party libraries - No UIKit App behavior: - Request microphone permission - Capture live microphone input - Analyze the input continuously - Display a real-time spectrum visualizer using vertical bars - Use a reasonable number of bars such as 32 or 48 - Normalize the values so the bars look visually stable - Add light smoothing to reduce flicker Architecture: - AudioAnalyzer: ObservableObject responsible for AVAudioEngine setup, audio tap, FFT, normalization, smoothing, and publishing spectrum data - SpectrumBarView: SwiftUI view that renders the bar graph - ContentView: owns AudioAnalyzer with @StateObject and starts/stops audio analysis - App entry point Implementation constraints: - Keep the project intentionally small - Avoid unnecessary protocols, managers, coordinators, or excessive abstraction - Prefer beginner-friendly naming - Add concise comments around important learning points - Publish UI updates on the main thread - Avoid expensive work on the main thread when possible - Keep the code structured for a technical article Deliverables: 1. Full source code for all files 2. File structure 3. Notes for Info.plist microphone permission key 4. Short explanation of key implementation decisions 5. Mention likely pitfalls Important: - The output must be real compilable Swift code - Do not use placeholder pseudocode - Do not over-engineer - Make the first version easy to iterate on after bugs are found このプロンプトで狙ったこと このプロンプトでは、単に「音声ビジュアライザーを作って」と依頼するのではなく、
次の点を最初から固定しています。 SwiftUIで作る AVAudioEngineを使う FFTはAccelerate系で行う 表示はバーグラフ形式にする 32〜48本程度の見やすい本数にする 正規化と平滑化を入れる 小さく読みやすい構成にする 特に重要なのは、最初から「小さく・動く・読みやすい」ことを優先している点です。 音声処理のような題材では、AIに自由度を与えすぎると
「一見すごそうに見えるが、実際には直しにくいコード」が出てくることがあります。 今回は技術記事化も前提にしているため、
完成度よりもまず 反復しやすさ を重視しました。 ここでのゴール この段階でのゴールは、完成品を一発で得ることではありません。 まず動く最初の版を出してもらう Xcodeでビルドする エラーや不具合を確認する 必要な修正を再度依頼する という流れを前提にしています。 今回のCodexの使い方は 一発生成型ではなく、反復改善型の使い方 です。 この方が実際の開発にも近く、記事としても再現しやすい構成になります。 実際になげてみる しばらく待ってみると以下のように ファイルが生成されます 更に実機でデバッグ実行してみる 特にエラーが起きることもなく以下のような画面が表示されます。 出力されたアプリの状況 何も手を加える事なく起動し、音声も取れていますが、常に左側のバーが高い状態のため、
ノイズをそのまま出力してる状態の様に見えます。 今の状態をまとめると ビルドOK 起動OK マイク入力OK ただし表示が期待どおりでない。 なので、ビルドエラーなどの対処はありませんでしたが、修正したい部分があったので、
「表示安定化」 を考えた上で修正を行っていきます。 考えられる原因 この現象の原因は、FFT(高速フーリエ変換:Fast Fourier Transform)結果の扱い方にありそうです。 FFTの出力は、そのまま使用すると非常に変動が大きく、
フレームごとに値が大きく上下します。 特にマイク入力では、 マイク自体のノイズ 空調音 微小な環境音 などもすべて数値として現れます。 そのため、FFTの値をそのまま表示するとスペクトラムが過敏に反応してしまうことがあります。 音声ビジュアライザーでは、解析の正確さだけでなく視覚的な安定性も重要になります。 そのため多くの実装では次のような処理が行われます。 値の平滑化(Smoothing) 微小ノイズの除去(Noise Floor) 表示用の正規化(Normalization) ここまでのまとめ 実際にAIに生成してもらったコードで、基本的な機能は実装できていましたが、
表示に不満が発生し、何が原因なのかを考えました。 この原因となる部分に関しては、
世間にあるアプリを見ても各社さまざまな調整がされており差が発生している部分であり、
こういった部分が人間が考慮したうえで調整を行う必要があります。 次回はこの問題をCodexに依頼することで修正していきます。 参考記事 生成AIと作るリアルタイム音声ビジュアライザー (準備編) -
生成AIと作るリアルタイム音声ビジュアライザー (準備編)
はじめに CoreAudioまわりのサンプルは、理解しようとすると少しハードルが高い領域です。
今回は題材として リアルタイム音声ビジュアライザー を選び、AIコーディングツール Codex を使って SwiftUI ベースのサンプルアプリを作ってみます。 この記事では、 SwiftUI AVAudioEngine Accelerate (FFT) Codex を使いながら、AIとペアプロする形でサンプルアプリを完成させていく過程をまとめます。 なお、最初から完成コードを出すのではなく、 AIに実装を依頼する ビルドエラーや挙動の問題を修正する 最終的に動くサンプルへ育てていく という流れで進めます。 今回はまず Codexに指示を出す前の準備までを扱います。 作るアプリ 今回作るアプリはシンプルです。 マイク入力の音をリアルタイムで解析し、周波数スペクトラムを表示するアプリです。 次のような構造で動作します。 Microphone
↓
AVAudioEngine
↓
Audio Buffer
↓
FFT(Accelerate / vDSP)
↓
Spectrum Data
↓
SwiftUI View 表示は以下のようなイメージになります。 █ ▇ ▅ ▃ ▄ ▆ █ ▇ ▅ ▃ ▄ ▆ █ なぜSwiftUIを使うのか 今回は UIKit ではなく SwiftUI を使用します。 理由は主に2つです。 UIコードを短くできる 音声ビジュアライザーの主役は 音声処理(Audio Processing) です。
UIKitを使うと、どうしても次のような実装が必要になります。 AutoLayout CALayer drawRect View更新管理 結果として UIコードの比率が大きくなり、本筋の音声処理から離れてしまうことを避けるためです。 SwiftUIなら、スペクトラム表示も非常にシンプルに書けます。 例えば、HStack の中で Rectangle を並べるだけでバー表示のベースを作ることができます。 HStack で横方向に配置 ForEach でスペクトラムデータをループ Rectangle でバーを描画 このように 少ないコード量でリアルタイムグラフの基礎が作れるため、今回はSwiftUIを使用します。 AI生成コードとの相性が良い 今回の記事では AIを使用して音声処理アプリを実装する をテーマにしています。 SwiftUIは宣言的UI(Declarative UI)のため、 UIKit AutoLayout CALayer操作 などと比較して AIが生成するコードの成功率が高い傾向があります。 特に次の点で相性が良いと感じています。 View構造がシンプル 状態管理が明確 UI更新がデータバインディング中心 UIKitの場合、AIが生成したコードでは次のような問題が起きることがあります。 AutoLayout制約が崩れる View更新のタイミングが不適切 CALayer描画処理が複雑になる 一方SwiftUIでは、UIの更新は基本的に 状態の変化によって自動的に反映されます。 そのため、 データ(スペクトラム配列)を更新する SwiftUIが自動で画面を更新する というシンプルな構造になります。 今回のサンプルでも、 マイク入力を取得する FFTでスペクトラムデータを作る 配列を更新する SwiftUIがグラフを再描画する という 非常に素直なデータフローを作ることができます。 結果として、AIが生成したコードをそのまま動かせる確率が高くなるため、
手動による修正や追加実装のコストを下げる事にも有用です。 プロジェクト作成 まずは XcodeでSwiftUIプロジェクトを作成します。 メニューから次を選択します。 File → New Project → App プロジェクト設定は以下のようにします。 Product Name : LiveAudioVisualizer Interface : SwiftUI Language : Swift Deployment : iOS 17+ プロジェクトを作成すると、最小構成のSwiftUIアプリが生成されます。 LiveAudioVisualizer ├ LiveAudioVisualizerApp.swift └ ContentView.swift この段階では、まだ音声処理のコードは存在していません。 マイク権限の設定 iOSでマイクを使用するアプリでは、
Info.plistに使用理由を記述する必要があります。 この設定が無い場合、アプリはマイクにアクセスできません。 SwiftUIテンプレートの場合、Info.plist がファイルとして見えないことがあります。
その場合は Target設定のInfoタブから追加します。 TARGETS → LiveAudioVisualizer → Info 追加するキーはこちらです。 Privacy - Microphone Usage Description 値は例えば次のようにします。 Microphone access is required for audio visualization. この文章は、ユーザーが初めてマイクアクセスを許可する際に表示されるメッセージになります。 まとめ 今回は、リアルタイム音声ビジュアライザーを作るための 準備段階として、 SwiftUIを採用した理由 Xcodeでのプロジェクト作成 マイク権限の設定 までを行いました。 この段階ではまだアプリとしての機能はほとんどありませんが、
音声入力を扱うiOSアプリの土台は整った状態になります。 次のステップでは、いよいよ AI(今回はCodex)に実装を依頼します。 具体的には、 マイク入力の取得(AVAudioEngine) 音声データの解析(Accelerate / FFT) スペクトラムデータの生成 SwiftUIでのリアルタイム表示 といった部分を、AIと協業する形で実装していきます。 また、AIが生成したコードは必ずしもそのまま動くとは限らないため、 ビルドエラーの修正 動作の調整 表示の安定化 といった作業も含めて、実際の開発フローに近い形で進めていきます。 次回は、Codexに渡した 実際のプロンプト と、
そこから生成されたコードを見ながら、実装を進めていきます。