Repository navigation
Pitch Shift Audio Quality Investigation
調査日: 2026-10-17。対象リビジョン: d3dd9b44d20ad787ae70caa5cc71d9570d889d71。以下はコード変更前の初期調査の記録である。その後の実装内容と検証状況はピッチ品質改善と出力ピーク保護の実装を参照。
報告された条件は、iPhone で音声ファイルを再生し、速度100%、EQなしでキーを約3半音下げると、ノイズが乗ったような、ガビガビした音が聞こえるというもの。キーを戻すと消え、同じようにキーを下げた Music Speed Changer では問題を感じないとのことだった。iPhone の機種・OS、音源、音量、音量の自動調整、出力先の設定は未確認。
調査では、ピッチ変換による周波数成分の変化と、変換後のピーク増加の両方を合成音で確認した。前者は音の荒れ、後者は後段での音割れにつながる原因候補である。報告音源と iPhone 実機では検証していないため、報告の直接原因や両者の寄与は確定していない。
現在の実装と仕様
- PlayerViewModel.swift:521–524 は半音数を100倍して渡す。−3半音は−300 centsで、単位の換算は正しい。キー操作によって速度や音量を変更する処理はない。
-
AudioEngineService.swift:90–104 の処理順は
player volume → AVAudioUnitTimePitch → 音量自動調整 → EQ → reverb → mixer。キー変更には Apple のAVAudioUnitTimePitchを使い、overlapは設定していない。エンジン再生成時も同じ既定値になる。 - TransformedAudioRenderer.swift:44–53 の調整済みコピーの保存も、同じ TimePitch の既定設定を使う。品質改善を実装する場合は、通常再生・エンジン再生成・書き出しの3か所を揃える必要がある。
- Apple の overlap 仕様 は既定8、範囲3〜32で、高い値ほど出力のアーティファクト(変換に伴う不要な音の変化)が減ると説明している。macOS 10.10/iOS 8以降で利用可能。Xcode 27のmacOS/iPhoneOS SDKヘッダーはCPU負荷への影響も明記している。
- Xcode 27の
AudioUnitParameters.hでは、同じパラメーターID 4をSmoothnessと呼び、処理フレーム密度とCPUコストが比例すること、打楽器の速度を下げる際には低い値がよい場合もあることを説明している。今回の実験では spectral coherence と transient preservation の値はともに1だった。「位相の保持が無効だから」という説明を裏付ける結果ではない。 - AudioLoudnessNormalizer.swift:79–81 は原音のピークから増幅上限を計算する。ピッチ変換後のピークは追跡せず、音声グラフに最終段のリミッターもない。音量の自動調整とEQはともに既定OFFだが、自動調整をONにしても変換後のピークが必ず収まるとはいえない。
オフライン実験
macOS 27.0.1(26A434)、Xcode 27.0(27A266a)、Swift 6で、実装と同じ順のノードを持つ独立した AVAudioEngine を作った。音量自動調整・EQ・reverbはバイパスし、速度は1.0とした。これはアプリ全体やiPhoneの実時間再生のテストではない。
入力は6秒、ピーク0.95の合成音4種(1 kHz正弦波、110 Hzの60倍音を足した帯域制限鋸歯状波、3音それぞれの12倍音を足した和音、減衰する短音の反復)。44.1/48 kHz、同一波形を左右に入れたステレオで、キー0/−3/+3半音、overlap 8/16/32を比較した。キー0はoverlap 8のみ。各条件で新しいエンジンを生成し、左右のうち左をfloat32で保存した。
音量1.0の56条件と、TimePitchの前の player.volume をアプリ既定の0.75にした56条件、計112条件のレンダリングが完了した。以下は−3半音の結果である。
| 48 kHzの帯域制限鋸歯状波、player音量0.75 | 出力ピーク | 全288,000サンプル中の絶対値1超過 |
|---|---|---|
| キー0、overlap 8 | 0.7125 | 0 |
| キー−3、overlap 8(現在の既定値) | 1.0184 | 412 |
| キー−3、overlap 16 | 1.0368 | 1,205 |
| キー−3、overlap 32 | 1.0442 | 1,269 |
既定の音量0.75でも、キーを下げることでフルスケールを超える入力例があった。最初と最後の1秒を除いた定常区間でも、overlap 8のピークは1.00494で上限を超えており、開始時だけの現象ではない。float出力には1を超える値が残っているので、この実験自体でクリップ済みの波形やiPhone出力の音割れを確認したわけではない。後段の減衰・整数化・デバイス出力によって結果は変わる。
周波数成分については、定常区間の1〜5秒にHann窓を掛け、FFTで計測した。入力に含まれる周波数を 2^(-3/12) 倍した周波数の±5 Hzを期待成分とし、その外の電力を全電力に対する比で算出した。以下の値は音量0.75の実測で、値が小さい(より負の値になる)ほど期待成分の外の電力が少ない。これはこの合成音用の簡易指標であり、音楽一般の聴感評価値ではない。
| −3半音の入力 | overlap 8 | overlap 16 | overlap 32 |
|---|---|---|---|
| 鋸歯状波、44.1 kHz | −52.0 dB | −64.9 dB | −68.5 dB |
| 鋸歯状波、48 kHz | −54.3 dB | −66.8 dB | −70.8 dB |
| 和音、44.1 kHz | −23.4 dB | −23.5 dB | −23.6 dB |
| 和音、48 kHz | −10.3 dB | −11.9 dB | −13.8 dB |
overlapを上げると規則的な倍音の入力では余分な成分が減ったが、和音では改善量が小さい例もあった。44.1 kHzの和音では、入力の164.8138 Hzを移調した理想値138.5913 Hzとは別の142.75 Hzに強いピークが現れ、overlap 32でも残った。±5 Hzの集計はこのずれを期待成分内に含めるため、上の数値だけで変換品質を評価することもできない。
以上から、overlapの増加は比較すべき改善候補だが、32への固定で報告が解消すると結論することはできない。ピーク超過は減らず、むしろ増える入力もあった。処理時間も増えており、iPhoneの実時間再生で処理が間に合うかを確認する必要がある。macOSの単回オフライン所要時間からiPhoneのCPU使用率は推定しない。
比較先アプリと追加の切り分け
同じApp IDの Music Speed Changer のApp Store説明 と iOS公式ヘルプ では、半音単位のピッチ変更、独立したTempo操作、Rateによる連動操作を確認した。Proのフォルマント補正 もある。今回確認したiOS向け一次公開情報から、採用アルゴリズム名は特定できなかった。Android版のアルゴリズム情報をiOS版の根拠には使わない。
次の確認を報告音源・同じiPhone・同じ出力先で行うと、対応を判断しやすい。
| 確認条件 | 判定に使う観測 |
|---|---|
| 速度100%、キー−3、EQ/音量自動調整OFFで、アプリ内音量を下げて比較 | 音割れが消えるか。単なる音量差で荒れが目立たなくなる可能性もあるため、必要なら録音を安全なレベルで音量合わせして比較する |
| 同じファイルのoverlap 8/16/32を、安全なピークと音量に揃えて比較 | 変換による荒れが減るか、和音・ボーカル・打楽器で副作用がないか |
| アプリ再起動後から−3で再生した場合と、再生中に0→−1→−2→−3と変更した場合 | 固定設定の音質問題か、変更操作や内部状態の問題か |
| 調整済みコピーにも同じ荒れがあるか | オフラインにも出る変換品質か、実時間処理・出力経路への依存か。再生と保存では接続フォーマットが異なるので、片方だけ正常でも原因を断定しない |
| 比較先をPitch −3、Tempo 100%、Rate連動なし、EQ等OFF、フォルマント補正OFFに揃える | 同じ処理目的・素材でアプリ間の音質差が残るか |
実装する場合は、まずoverlap比較と変換後のヘッドルーム確保を別々に評価する。前者で改善が不足するなら、別のピッチ変換処理を候補に含める。修正の完了条件は、報告区間での聴感改善、ピーク制御の実測、iPhone/iPad/macOSでの連続再生とキー変更、エンジン再生成後、調整済みコピーの再読み込みまでの一致とする。
今回未検証なのは、報告者の実音源・iPhone実機・聴感、動的なキー変更、実ステレオの位相、出力先ごとのサンプルレート変換、処理期限超過による音切れ、変換末尾の排出である。実験は入力フレーム数で終了し、末尾を追加排出していないため、曲末尾の長さや品質評価には使わない。アプリコードは変更していないため、アプリ全体のビルド・単体テスト・lintは実行せず、独立した実験プログラムをコンパイルして実行した。
実験ソース、解析スクリプト、結果JSON、再実行方法は、WikiのGitリポジトリ内の benchmarks/pitch-quality-2026-10-17 に保存した。再実行方法のMarkdown原文を参照。生成音声はスクリプトから再生成でき、Wikiには含めていない。