如何将SoundEffectInstances组合成一个新的Sound File / mp3或wav

时间:2011-12-04 12:02:43

标签: c# windows-phone-7

我正在使用新的WindowsPhone平台。我有一些SoundEffectInstance的内容,我想将它组合成一个新的单个Sound文件(SoundEffectInstance,SoundEffect或MediaElement,它没关系。)然后我想将该文件作为mp3保存到手机中。

我该怎么做?通常,我会尝试将所有文​​件发送到bytearray,但我不确定这是否是正确的方法,或者如何将bytearray转换为MP3格式的声音。

所以例如我有SoundEffectInstance soudBackground,播放时间为0 - 5秒。然后我将SoundEffectInstance chime播放3-4秒,SoundEffectInstance前景播放时间为3.5到7秒。我想将所有这些组合成一个持续7秒的单个mp3文件。

1 个答案:

答案 0 :(得分:1)

您要在此处完成两项任务:

  • 将多个声音文件合并为一个声音文件
  • 将生成的文件另存为MP3。

据我所知,到目前为止,您将对第2项提出一些挑战。到目前为止,我还没有找到纯粹的.Net MP3编码器。我找到的所有依赖于P / Invokes到本机代码(当然这在手机上不起作用)。

至于组合文件,您不希望将它们视为SoundEffectInstance。该类仅用于播放,它将声音文件的大部分细节抽象出去。相反,您需要将声音文件视为整数数组。我将假设所有三个声音文件的采样率完全相同,并且这些是16位录音。我也将假设这些波形文件以单声道录制。我现在保持场景简单。在掌握了这个更简单的场景之后,您可以使用立体声和各种采样率对其进行扩展。

波形文件的前48个字节只是标题。跳过它(暂时)并将波形文件的内容读入自己的数组。一旦阅读完毕,我们就可以开始将它们混合在一起。忽略你想要开始播放这些声音的时间差,如果我们想要开始生成一个样本,这是三者的组合结果,我们可以通过将声音文件数组中的值一起添加并将其写入数组来实现保持我们的结果。但是有一个问题。 16位数字最多只能达到32,767(最低可达-32,768)。如果所有三种声音的组合值都超出了这些限制,那么你的失真就会非常糟糕。处理此问题最简单(但不一定是最好)的方法是考虑将播放的最大同时发出的声音并相应地缩小值。从3.5秒到4秒标记,您将播放所有三种声音。所以我们将通过除以3进行扩展。另一种方法是使用超出此范围的数据类型对声音样本求和,然后在将它们混合在一起时将值归一化回到此范围。

让我们定义一些参数。

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