Как объединить SoundEffectInstances в новый звуковой файл / mp3 или wav - PullRequest
2 голосов
/ 04 декабря 2011

Я работаю на новой платформе WindowsPhone.У меня есть несколько экземпляров SoundEffectInstance, которые я хотел бы объединить в новый единственный звуковой файл (SoundEffectInstance, SoundEffect или MediaElement, это не имеет значения.) Затем я хочу сохранить этот файл в формате mp3 на телефон.

Как мне это сделать?Обычно я пытаюсь отправить все файлы на байт-массив, но я не уверен, что это правильный метод, или как конвертировать байт-массив в звук формата MP3.

Так, например, у меня естьSoundEffectInstance soudBackground, воспроизведение от 0 до 5 секунд.Затем у меня есть звуковой сигнал SoundEffectInstance, играющий от 3 до 4 секунд, и передний план SoundEffectInstance, играющий от 3,5 до 7 секунд.Я хочу объединить все это в один mp3-файл, который длится 7 секунд.

1 Ответ

1 голос
/ 20 декабря 2011

Здесь есть две задачи, которые вы пытаетесь выполнить:

  • Объединение нескольких звуковых файлов в один звуковой файл
  • Сохранение полученного файла в формате MP3.

Насколько я обнаружил, с пунктом 2 у вас возникнет немало проблем. На сегодняшний день я не нашел чистого MP3-кодера .Net.Все те, которые я нахожу, полагаются на P / Вызывает нативный код (который, конечно, не будет работать на телефоне).

Что касается объединения файлов, вы не хотите рассматривать их как 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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...