コメントであなたの説明に従って答えます:
2つのmp3ファイル(5秒のa.mp3と7秒のb.mp3)があり、7秒の長さのc.mp3を生成するためにそれらを混合したいとします。
コメントで述べたように、iOSの詳細を説明することはできませんが、使用するプラットフォームやライブラリに関係なく、このプロセスを実行するために論理的に何が必要かを確認できます。簡単なC++スニペットを使用して説明します。ただし、a.mp3(以降A)をb.mp3(以降B)のどこかにミックスして(たとえば、AをBの先頭にミックスして)、結果のオーディオクリップCを生成するように聞こえます。
S16_LE
まず第一に、それらはMP3ファイルであり、WAVやRAWやAIFFなどの他の非圧縮PCM形式ではないと述べたので、最初にAとBをPCM(CDオーディオ形式-署名付き16)などの非圧縮形式に変換する必要があります。-ビット整数サンプル、リトルエンディアン)。これは、AとB、したがってCのサンプル値の配列(ステレオオーディオの場合は左右のチャンネルがインターリーブされている)で作業することを意味します。ミキシングが完了したら、オプションでMP3に再エンコードします。
ライブラリを使用してファイルのエンコード/フォーマットの問題を処理する必要がありますが、それらを使用すると、直接記録または再生用のシステムインターフェイスを含め、すべてが生成(つまり、読み取り時)または期待(つまり、書き込み)基本的に、これと同じ基本的な非圧縮PCMサンプルストリーム形式。一般的な開発では、ユビキタスlibsndfile
Cライブラリは、おそらくフォーカシング。
簡単にするために、モノラルサウンドクリップAとBのみを検討します(つまり、AとBのサンプル値の単純な配列であり、インターリーブされた左/右チャネルについて心配する必要はありません)。重要な場合は、各ステレオチャンネルを個別に検討することで(A.leftはB.leftとミックスし、A.rightはB.rightとミックスします)、概念をステレオに簡単に拡張できます。特定のAとBがステレオであるが、Cがステレオである必要がない場合は、アプリケーションに応じて、両方の入力オーディオクリップを事前にモノラルに変換することもできます。
さらに、通常、オーディオサンプルを浮動小数点値として処理する方が簡単なのでlibsndfile
、非圧縮サンプル形式を[-1.0の範囲の浮動小数点に変換します(または、通常、オーディオファイルライブラリが自動的に行います)。 、+ 1.0]、ここで、1.0の絶対値は可能な限り最大のサンプル値を表し、0.0は無音を表します。これらのサンプル値は、時間の経過に伴う(つまり、アレイ全体での)任意のオーディオ波形の変化を構成します。
まず、ミキシングする前に、十分な「ヘッドルーム」(出力のクリッピングを防ぐ)があることを確認する必要があります。なんで?ミキシングでは、信号の重ね合わせ(加算)の原理を使用して、信号/サウンドを結合します。オーバーラップするサンプルごとにAとBを加算するため、対応するサンプルのAとBの合計が、混合された出力サンプルが「クリップ」する可能性があります。 1.0を超えるか、-1.0を下回ります。
それぞれの入力レベルと、それらの音量比を維持するか、単にそれらを均等に組み合わせるか(またはステレオで作業していて、Aの最も大きいチャンネルのいずれかを使用するかどうか)に応じて、クリッピングを防ぐ方法がいくつかあります。またはBを基準点として使用します。これはステレオについて最後に聞くものです)。
最も単純なルートを取り、AとBの両方のボリュームを正規化して、フルスケールの半分(0.5)以下でピークに達するようにします。これにより、それらを足し合わせたときにクリップしないようになります(つまり、混合出力サンプルがなくなることはありません)。範囲[-1.0、+ 1.0])を超えています。2つの入力ではなく、この方法で同時にミキシングされる3つの入力オーディオクリップX、Y、およびZがある場合、ピーク時のフルスケールの1/3(0.33)にそれぞれを正規化します。
それぞれのサンプルバッファ/アレイを反復処理し、それぞれの最大サンプル値を決定することにより、AA_peak
とBの両方のピーク値を見つけます。B_peak
[従うべきコード。]
それぞれのピーク値に対する乗算がハーフスケールになるように、スケーリング値A_scale
と各サンプルバッファAおよびBをそれぞれ決定します。B_scale
[従うべきコード。]
A_scale * A_peak == 0.5
B_scale * B_peak == 0.5
エルゴ:
A_scale = 1 / (2 * A_peak)
B_scale = 1 / (2 * B_peak)
A_scale
これで、サンプルバッファーAとB全体にそれぞれとを掛けることができます。B_scale
これらはそれぞれ正確にハーフスケールでピークになるように正規化され、2つの混合サンプルがフルスケールを超えることはありません。つまり、AとBの最大値がサンプルに対して整列されている場合でも、それらのスケーリングおよび合計されたミックス出力は正確に1.0になり、それより大きくなることはありません。このようなスケーリング係数は、「ゲイン」と呼ばれることがよくあります。
繰り返しになりますが、ミキシング時に2つ以上のサンプルバッファー(オーディオクリップ)間のゲインを正規化する方法は複数ありますが、これはデモンストレーションで最も単純で最も一般的な方法です。さらに、N個の異なるオーディオクリップを一緒にミキシングするのに簡単に適応でき(上記のように)、わずかに単純化して、サンプルストリームのリアルタイムミキシングに適応します(オーディオクリップのサンプルバッファ全体が利用できず、サンプル処理が行われます)記録時によくあるように、チャンクで)。
これで、ミキシングに取り掛かることができます。
この場合、A(5秒)はB(7秒)内に収まるため、ミックスをBに直接出力できますが、一般的には、ミックスを別のサンプルバッファーC(7秒)に出力し、入力AとBは浮動小数点サンプルバッファとして変更されていません(おそらく再利用される可能性があります)。
サンプルA_len
カウントのAの長さ(簡単に決定されます。基本的には時間とサンプルレートのみに依存しますが、ライブラリはファイルをロードするときに通知します)、B_len
B、および出力Cの場合と同様に、C_len == B_len
、B_len > A_len
問題の説明にあるからです。
ミックス出力であるCを割り当てます。
unsigned int C_len = max(A_len, B_len);
double C[] = new double[ C_len ];
AとBのサンプルの絶対値のピークを見つけます。
double A_peak = -1.0, B_peak = -1.0;
for (unsigned int i = 0; i < A_len; ++i) A_peak = max( A_peak, fabs(A[i]) );
for (unsigned int i = 0; i < B_len; ++i) B_peak = max( B_peak, fabs(B[i]) );
AとBのハーフスケール正規化ゲインを見つけます。
double A_scale = 1 / ( 2 * A_peak );
double B_scale = 1 / ( 2 * B_peak );
AをBに混ぜてCに入れます。
assert(A_len <= B_len);
assert(B_len == C_len);
unsigned int x = 0;
for (; x < A_len; ++x)
C[x] = A_scale * A[x] + B_scale * B[x]; // actual mixing of A and B, finally
for (; x < B_len; ++x)
C[x] = B_scale * B[x]; // as if A[x] were zero & no abrupt gain change
浮動小数点バッファーAとBは、混合と正規化の後も変更されていないことに注意してください。
Aは、混合されていないすべての場所でゼロ/サイレントと考えることができます。
(ここで想定されている開始時ではなく)B内の任意のオフセットでAのミキシングを開始したい場合は、時間オフセットに対応するサンプル数(t_offset
秒単位、s_offset = t * sample_rate
整数サンプル単位)を単純に計算し、x == s_offset
上記のループ構成のミックスにAを含め始めます。[オーバーフローを防ぐためにそれを想定していs_offset + A_len <= C_len
ます。]
多くの可能性があるため、混合入力を正規化するアプリケーション固有の方法を試すことをお勧めします。たとえば、それぞれのピークを個別に計算するのではなく、AとBのサンプルの合計のピークを計算した場合はどうなりますか(基本的に最初に混合し、後で修正します)。この[より良い]テクニックが不可能なのはいつですか?
最後に、信号をミキシングするときはいつでも、ミキシングが開始および終了する遷移ポイント(たとえば、クリック)(たとえば、Aが終了するがBがCに入り続けるポイント)でアーティファクトが発生する可能性があります。ここでは、これは比較的リスクが低いです。ただし、このようなアーティファクトの一般的な解決策は、ミックスへの入力の入力/終了の短時間のフェードインとフェードアウトを行うことです。これにより、混合波形をスムージングすることでアーティファクトを排除し、聞こえないほど速く実行できます。 。