1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

検査は6通りとも通っていたのに、画面から押すと落ちた(AVAudioEngineの二重installTap)

1
Last updated at Posted at 2026-09-06

はじめに

私は、iPhoneアプリ「チェックワンツー - ハウリングチェック&アナライザー」を開発しています。App Storeのサブタイトルは「バンド練習の録音とサウンドチェック」です。

入力の大きさ(マイクに入る音の大きさ)を自動で合わせる機能を作りました。マイクの音を5秒だけ聞いて、ちょうどいい倍率をアプリが自分で決めるボタンです。検査(動作を確かめるテスト)は6通り書いて、6通りとも通っていました。

そのあと、私が機能をひととおり確かめようとして、画面を開いて指でボタンを押しました。

アプリは1回目で落ちました。

出たエラー

required condition is false: [AVAEGraphNode.mm:826:CreateRecordingTap: (nullptr == Tap())]
*** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio'

このエラーは、同じバスに、タップが2つ付こうとしている、という意味です。

installTap は、マイクに流れている音を横から受け取る口を1つ付けるものです。同じ口(bus 0)に、2つは付けられません。

これは Swift の do / catch では受けられません。Objective-C の例外なので、try を書いても止まらず、その場でアプリが終わります。

タップが、すでに1つ付いていた

この画面には、入力の大きさを出すメーターがあります。画面を開いた時点で動き始めます。

.onAppear { rec.待機を始める() }

待機を始める() は、bus 0 に installTap します。画面を開いた時点で、もうタップが1つ付いています。

そこでボタンを押すと、合わせる処理がもう1つ installTap します。この2つ目を付けようとした時点で、上のエラーが出てアプリが終わります。

この1行は、前の日に足したもの

私が .onAppear を足したのは前の日です。理由はこれでした。

  • メーターを動かす処理は書いてあったのに、どこからも呼んでいなかった(呼んでいたのは検査用の画面だけ)
  • そのため、[録音開始]を押すまでメーターが1mmも動かない
  • 入力の大きさを合わせるのは、[録音開始]を押す前なので、いちばん見たいときに何も見えていなかった

この直し自体は正しいもので、いまも入っています。

ただ、これで画面を開いた時点でタップが1つあるという状態が新しくできました。その前提で書かれていなかった、入力の大きさを合わせる関数が、次の日に落ちました。

直した所ではなく、その直しを前提にしていなかった別の場所が壊れます。

なぜ検査では出なかったか

入口の guard はこう書いてありました。

guard !録音中, !合わせ中 else { return }

録音中でないこと、合わせ中でないことは見ています。メーターが動いているかどうかは、どこにも入っていません。

そして検査は、画面を開かずに、この関数を直接呼んでいました。メーターは動いていません。タップは1つも付いていません。だから通ります。

画面を開いてから押す、という順番を、一度も試していませんでした。

押す前に外して、終わったら付け直す

私は guard を増やしていません。押す前に外して、終わったら元どおり付け直す形にしました。

let 待機していた = 待機中
do {
    try セッションを整える()
    let input = engine.inputNode
    if 待機していた { input.removeTap(onBus: 0); 待機中 = false }
    let fmt = input.inputFormat(forBus: 0)
    input.installTap(onBus: 0, bufferSize: 4096, format: fmt) { buf, _ in
        // 5秒分の大きさを集める
    }
    try engine.start()
} catch {
    合わせ中 = false; 状態文 = ""
    if 待機していた { 待機を始める() }   // ★失敗しても、メーターは元に戻す
    完了("測定できませんでした。もう一度お試しください。")
    return
}

5秒たったあとも、同じように戻します。

self.engine.inputNode.removeTap(onBus: 0)
self.engine.stop()
self.合わせ中 = false
// ★元どおりメーターを動かす(押す前と同じ状態に戻す)
if 待機していた { self.待機を始める() }

大事なのは catch の中にも戻す処理を置くことです。ここを書かないと、合わせる処理を始められなかったとき(catch に入ったとき)、メーターが止まったままになります。画面は生きているのに数字だけ動かなくなります。

1か月前に、同じ地雷を踏んでいた

私がこのアプリのコードを検索したら、こう書いてある行が4つありました。

do { try engine.start() } catch {
    engine.inputNode.removeTap(onBus: 0)   // 残すと再試行で二重タップ即クラッシュ(既知地雷照合2026-07-17)

同じ落ち方を1か月前にもう経験していて、注意書きまで残しています。それでも今回また踏んだのは、形が違ったからでした。

  • 前回 … engine.start() に失敗して、もう一度やり直したときにタップが2つになる
  • 今回 … 失敗していない。別の場所がすでにタップを付けているところへ、正常に1つ足す

前回の対策は「やり直す前に外す」でした。今回は失敗していないので、その道を通りません。

「二重タップに気をつける」と書いてあっても、次に踏む形は違います。

何をすれば防げたか

installTap を呼ぶ場所を数えました。このアプリには8か所ありました(1つのファイルに5か所あるものもあります)。
どれも「自分が付ける前に、誰かが付けていないか」を見ていませんでした。

いま考えると、確かめ方は2つあったと思います。

  1. 画面から指で押す順番で試す。関数を直接呼ぶ検査では、画面の状態が再現されません
  2. installTap を呼ぶ場所を全部数えて、同じバスを使っているものを並べる。検索するだけで出ます

おわりに

検査が6通り通っていたことは、この機能が使えることを何も保証していませんでした。

検査は、私が書いた呼び方でしか呼びません。画面を開いて、待って、押す──という順番は私が検査に書かなかったので、一度も走っていませんでした。

「動いた」と「使える」は違うとはよく言われますが、今回は「検査が全部通った」と「使える」も違いました。
落ちたのは、そのアプリの看板になる機能でした。

出典

エラー文と行番号は、手元で実際に出たものをそのまま載せています。

チェックワンツーを試す

この記事で紹介した録音入力の大きさを自動で合わせる機能は、チェックワンツーの「オーディオ録音(ステレオ)」で、ご自身の現場でもすぐ試せます。

インストールは無料、会員登録も不要で、iPhoneの内蔵マイクだけで使えます。

フル版(買い切り1,200円・7日間無料体験あり)なら、5分を超える本番も1本で録れて何件でも残せるので、まずはApp Storeから試してみてください。

App Store からダウンロード

公式サイト(使い方とマニュアル)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?