Генерация файлов WAV
Я пытаюсь программно создать аудиофайл WAV, который содержит n секунд стандартной британской мелодии звонка, которую вы слышите, когда набираете номер телефона.
Я нашел источник, который документирует частоты и длительность тона, который представляет собой синусоидальную волну двух частот: 400 Гц и 450 Гц.
Код, который я написал, генерирует WAV-файл PCM в правильном формате, который я могу воспроизводить с помощью аудиоплеера, такого как Windows Media Player, однако генерируемый им тон звучит на гораздо более низкой частоте, чем должно быть, поэтому не надо Я думаю, что у меня есть права.
Это то, что я сейчас делаю:
var duration = 10;
var bitsPerSample = 8;
var samplesPerSec = 8000;
var f1 = 400;
var f2 = 450;
var pattern = new[] {
TimeSpan.FromMilliseconds(400),
TimeSpan.FromMilliseconds(200),
TimeSpan.FromMilliseconds(400),
TimeSpan.FromMilliseconds(2000)
};
var wavdata = new byte[duration * samplesPerSec]; // 10 seconds of wav data @ 8000 samples per sec, 8 bits per sample, 1 channel
// Loop through each sample
for (var i = 0; i < wavdata.Length; i = i + (bitsPerSample / 8)) {
// Get time in seconds of the current sample
var time = Convert.ToDouble(i) / (Convert.ToDouble(bitsPerSample) / 8) / samplesPerSec;
// Calculate the on off pattern
var onoff = 0;
var timeMilliseconds = time * 1000;
var p = 0;
while (timeMilliseconds >= 0) {
timeMilliseconds = timeMilliseconds - pattern[p].TotalMilliseconds;
onoff = onoff == 1 ? 0 : 1;
if (++p >= pattern.Length) p = 0;
}
// Calculate the sample: (sin(time * 400) * 128 + sin(time * 450) * 128)) / 2
var sample = onoff * (((Math.Sin(time * f1) * 128) + (Math.Sin(time * f2) * 128)) / 2);
// Store sample
wavdata[i] = Convert.ToByte(sample + 128);
}
Как вы можете видеть формулу, которую я использую:
sin(time-of-sample * frequency) * amplitude
который я использую дважды для двух комбинированных частот:
sin(time * 400) * 128
sin(time * 450) * 128
Затем я складываю их вместе и делю на 2, чтобы получить среднее значение. Затем я умножаю на 1 или 0, чтобы получить тишину между тонами, чтобы получить звук звонка. И, наконец, я смещаю выборку на 128, когда сохраняю значение в массиве данных, так как данные файла WAV представлены как плюс или минус данные.
Что я делаю неправильно? Почему тон, который это генерирует, намного ниже по высоте, чем ожидалось?
1 ответ
Ты забыл Пи. Синусоидальные циклы от 0...2π, а не 0..1:
var sample = onoff * (((Math.Sin(2 * Math.Pi * time * f1) * 127) + (Math.Sin(2 * Math.Pi * time * f2) * 127)) / 2);
Также обратите внимание, что я изменил 128 с на 127 с, потому что ваше преобразование с плавающей запятой в байты будет переполнено, если ваш грех станет слишком близким к 1 или -1. Вероятно, есть лучший способ сделать это, не жертвуя диапазоном, но это, вероятно, не имеет значения для того, что вы делаете.
Если я могу прокомментировать: код делает слишком много преобразований единиц. Время в секундах и миллисекундах сбивает с толку. И "образец" должен быть в диапазоне от -1 до 1, а затем выполнить преобразование в байт в качестве отдельного шага. Умножая на 128 внутри этой строки, он смешивает концепцию байтов с вычислением звука, что немного сбивает с толку.