首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将SoundEffectInstances组合成新的声音文件/mp3或wav

如何将SoundEffectInstances组合成新的声音文件/mp3或wav
EN

Stack Overflow用户
提问于 2011-12-04 20:02:43
回答 1查看 1K关注 0票数 2

我正在开发新的WindowsPhone平台。我有几个SoundEffectInstance,我想合并成一个新的单一声音文件(无论是SoundEffectInstance,SoundEffect或MediaElement,这并不重要)。然后,我想将该文件作为mp3保存到电话中。

我该怎么做?通常,我会尝试将所有文件发送到一个字节数组,但我不确定这是不是正确的方法,也不确定如何将字节数组转换为MP3格式的声音。

例如,我有SoundEffectInstance soudBackground,播放时间为0-5秒。然后我让SoundEffectInstance铃声播放3-4秒,SoundEffectInstance前台播放3.5到7秒。我想将所有这些合并到一个持续7秒的mp3文件中。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-12-20 23:53:28

这里有两个您要尝试完成的任务:

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

到目前为止,我发现你在使用第二项时会遇到一些挑战。到目前为止,我还没有找到一个纯.Net MP3编码器。我找到的所有方法都依赖于原生代码的P/Invoke(这当然不能在手机上工作)。

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

wave文件的前48个字节只是标题。跳过这个部分(现在),将wave文件的内容读入它们自己的数组中。一旦它们都读完了,我们就可以开始把它们混合在一起了。忽略你想要开始播放这些声音的时间差,如果我们想要开始产生一个样本,这是所有三个声音的组合结果,我们可以通过将声音文件数组中的值加在一起,并将其写到一个数组中来保存我们的结果。但是有一个问题。16位数字只能上升到32,767 (下降到-32,768)。如果这三种声音的总和超过了这些限制,你就会得到非常严重的失真。处理此问题的最简单(但不一定是最好)方法是考虑将播放的最大同时声音数,并相应地缩小这些值。从3.5秒到4秒,您将播放所有三种声音。因此,我们将通过除以3来进行缩放。另一种方法是使用可以超出此范围的数据类型对声音样本进行求和,然后在完成将它们混合在一起时将值归一化到此范围。

让我们定义一些参数。

代码语言:javascript
复制
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];

一旦读取了声音文件并准备好接收结果文件的数组,就可以开始渲染了。我们有几种方法可以做到这一点。这里有一个:

代码语言:javascript
复制
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来确保结果数组中填充了0(我相信它是默认的,但我仍然喜欢显式地将它设置为0),然后为您希望在结果中包含的每个声音调用RenderSound()。在渲染完声音之后,调用RangeAdjust来标准化声音。剩下的就是把它写到一个文件中。你需要从整型转换回短整型。

代码语言:javascript
复制
short[] writeBuffer = new short[ResultantSound.Length];
for(int i=0;i<writeBuffer.Length;++i)
   writeBuffer[i]=(short)ResultantSound[i];

现在,混合声音已准备就绪,可以写入文件。只缺少一件事,在写文件之前,你需要写48字节的wave头。我在这里编写了如何做到这一点的代码:http://www.codeproject.com/KB/windows-phone-7/WpVoiceMemo.aspx

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8375149

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档