Генерация файлов 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 внутри этой строки, он смешивает концепцию байтов с вычислением звука, что немного сбивает с толку.

Другие вопросы по тегам