您在这里尝试完成两项任务:
- 将多个声音文件合并为一个声音文件
- 将生成的文件另存为 MP3。
就我目前所发现的而言,您将在第 2 项上遇到很多挑战。迄今为止,我还没有找到纯 .Net MP3 编码器。我发现的所有代码都依赖于 P/Invokes 到本机代码(这当然不适用于手机)。
至于合并文件,您不想将它们视为 SoundEffectInstance。该类仅用于播放,它抽象了声音文件的大部分细节。相反,您需要将声音文件视为整数数组。我将假设所有三个声音文件的采样率完全相同,并且这些都是 16 位录音。我还将假设这些波形文件是以单声道录制的。我现在保持场景简单。在您掌握了这个更简单的场景之后,您可以使用立体声和各种采样率对其进行扩展。
波形文件的前 48 个字节只是标题。跳过它(暂时)并将波形文件的内容读入它们自己的数组。一旦它们都被读取,我们就可以开始将它们混合在一起。如果我们想开始生成一个样本,它是所有三个的组合结果,则忽略开始播放这些声音的时间差异,我们可以通过将声音文件数组中的值相加并将其写入数组来实现保持我们的结果。但是有一个问题。 16 位数字最多只能达到 32,767(下降到 -32,768)。如果所有三种声音的组合值超出这些限制,您将得到非常糟糕的失真。处理这个问题的最简单(尽管不一定是最好的)方法是考虑将播放的同时声音的最大数量并相应地缩小值。从 3.5 秒到 4 秒,您将播放所有三种声音。所以我们将通过除以三来缩放。另一种方法是使用可以超出此范围的数据类型对声音样本求和,然后在将它们混合在一起后将值归一化回此范围。
让我们定义一些参数。
int SamplesPerSecond = 22000;
int ResultRecordingLength = 7;
short[] Sound01;
short[] Sound02;
short[] Sound03;
int[] ResultantSoundBuffer;
short[] ProcessedResultSoundBuffer;
//Insert code to populate sound array's here.
// Sound01.Length will equal 5.0*SamplesPerSecond
// Sound02.Length will equal 1.0*SamplesPerSecond
// Sound03.Length will equal 3.5*SamplesPerSecond
ResultantSound = new int[ResultRecordingLength*SamplesPerSecond];
一旦您读取了声音文件并准备好接收结果文件的数组,您就可以开始渲染了。我们有几种方法可以解决这个问题。这是一个:
void InitResultArray(int[] resultArray)
{
for(int i=0;i<resultArray.Length;++i)
{
resultArray[i]=0;
}
}
void RenderSound(short[] sourceSound, int[] resultArray, double timeOffset)
{
int startIndex = (int)(timeOffset*SamplesPerSecond);
int readIndex = 0;
for(int readIndex=0;((readIndex<sourceSound.Length)&&(readIndex+sourceSound<resultArray.Length;++readIndex)
{
resultArray[readIndex+startIndex] += (int)sourceSound[readIndex];
}
}
RangeAdjust(int[] resultArray)
{
int max = int.MinimumValue;
int min = int.MaximumValue;
for(int i=0;i<resultArray;++i)
{
max = Math.Max(max, resultArray[i]);
min = Math.Min(min, resultArray[i]);
}
//I want the range normalized to [-32,768..32,768]
//you may want to normalize differently.
double scale = 65536d/(double)(max-min);
double offset = 32767-(max*scale);
for(int i=0;i<resultArray.Length;++i)
{
resultArray[i]= (scale*resultArray[i])+offset;
}
}
您将调用 InitResultAttay 以确保结果数组填充为零(我相信这是默认情况下,但我仍然更喜欢将其显式设置为零),然后为您想要的每个声音调用 RenderSound() 结果.渲染声音后,调用 RangeAdjust 来标准化声音。剩下的就是将其写入文件。您需要从整数转换回短裤。
short[] writeBuffer = new short[ResultantSound.Length];
for(int i=0;i<writeBuffer.Length;++i)
writeBuffer[i]=(short)ResultantSound[i];
现在混合的声音已经准备好写入文件了。只缺少一件事,您需要在写入文件之前写入 48 字节的波头。我在这里写了如何做到这一点的代码:http://www.codeproject.com/KB/windows-phone-7/WpVoiceMemo.aspx