C # создать 24/32-битный звуковой файл WAV - PullRequest
0 голосов
/ 11 февраля 2019

Я следую этому руководству в блоге Microsoft Developer: https://blogs.msdn.microsoft.com/dawate/2009/06/24/intro-to-audio-programming-part-3-synthesizing-simple-wave-audio-using-c/

Учебник называется «Введение в звуковое программирование», и я выполнил все шаги, но, вероятно, что-то пропустил.

Это фрагмент конфигурации, извлеченный из класса WaveFormatChunk в его методе омонимов:

    sChunkID = "fmt ";
    dwChunkSize = 16;
    wFormatTag = 1;
    wChannels = 2;
    dwSamplesPerSec = 44100;
    wBitsPerSample = 16;
    wBlockAlign = (ushort)(wChannels * (wBitsPerSample / 8));
    dwAvgBytesPerSec = dwSamplesPerSec * wBlockAlign;

wBitsPerSample устанавливает битовую глубину сгенерированной функции, довольно простую до сих пор.

Затем, запустив программу, все работает с этими настройками.Генерация частоты 1 Гц с амплитудой 32760 при 16 бит / 44,1, например, это результат:

1 Гц, 16 бит 44,1 к

И это, очевидно, правильный вывод.

Теперь я процитирую это:

мы используем массив шорт, потому что у нас есть 16-битные выборки, как указано в блоке формата.Если вы хотите переключиться на 8-битный звук, используйте массив байтов.Если вы хотите использовать 32-битное аудио, используйте массив с плавающей точкой.

Говоря о shortArray в WaveDataChunk классе

public class WaveDataChunk
{
    public string sChunkID;     // "data"
    public uint dwChunkSize;    // Length of header in bytes
    public short[] shortArray;

Затем, для 32-битного аудио,изменение shortArray на число с плавающей точкой:

public float[] shortArray;

и wBitsPerSample на 32:

wBitsPerSample = 32;

Это результат: 1 Гц, 32 бита, 44,1 К

Практически частота удваивается, и записывается только половина времени.Что я сделал не так??Что мне делать?

1 Ответ

0 голосов
/ 11 февраля 2019

Когда вы используете IEEE с плавающей запятой, диапазон должен быть -1.1.1 (вместо целого числа min..max).Кроме того, тогда формат больше не MS PCM (1), а IEEE FLOAT (3).

Проблема со статьей состоит в том, что определения ее фрагментов инициализируются без учета формата IEEE FLOAT.Вот мое предложение сделать код более общим (буквально);чанки будут инициализировать поля (которые я сделал только для чтения) в конструкторе в зависимости от выбранного типа:

public class WaveFormatChunk<T> where T: struct, IConvertible
{
    public readonly string sChunkID;         // Four bytes: "fmt "
    public readonly uint dwChunkSize;        // Length of chunk in bytes
    public readonly ushort wFormatTag;       // 1 (MS PCM)
    public readonly ushort wChannels;        // Number of channels
    public readonly uint dwSamplesPerSec;    // Frequency of the audio in Hz... 44100
    public readonly uint dwAvgBytesPerSec;   // for estimating RAM allocation
    public readonly ushort wBlockAlign;      // sample frame size, in bytes
    public readonly ushort wBitsPerSample;    // bits per sample

    /// <summary>
    /// Initializes a format chunk. Supported data types: byte, short, float
    /// </summary>
    public WaveFormatChunk(short channels, uint samplesPerSec)
    {
        sChunkID = "fmt ";
        dwChunkSize = 16;
        wFormatTag = typeof(T) == typeof(float) || typeof(T) == typeof(double) ? 3 : 1;
        wChannels = channels;
        dwSamplesPerSec = samplesPerSec;
        wBitsPerSample = (ushort)(Marshal.SizeOf<T>() * 8);
        wBlockAlign = (ushort)(wChannels * ((wBitsPerSample + 7) / 8));
        dwAvgBytesPerSec = dwSamplesPerSec * wBlockAlign;            
    }
}

public class WaveDataChunk<T> where T: struct, IConvertible
{
    public readonly string sChunkID;     // "data"
    public readonly uint dwChunkSize;    // Length of data chunk in bytes
    public readonly T[] dataArray;  // 8-bit audio

    /// <summary>
    /// Initializes a new data chunk with a specified capacity.
    /// </summary>
    public WaveDataChunk(uint capacity)
    {
        dataArray = new T[capacity];
        dwChunkSize = (uint)(Marshal.SizeOf<T>() * capacity);
        sChunkID = "data";
    }   
}

public void FloatWaveGenerator(WaveExampleType type)
{          
    // Init chunks
    header = new WaveHeader();
    format = new WaveFormatChunk<float>(2, 44100);
    data = new WaveDataChunk<float>(format.dwSamplesPerSec * format.wChannels);            

    // Fill the data array with sample data
    switch (type)
    {
        case WaveExampleType.ExampleSineWave:
            double freq = 440.0f;   // Concert A: 440Hz

            // The "angle" used in the function, adjusted for the number of channels and sample rate.
            // This value is like the period of the wave.
            double t = (Math.PI * 2 * freq) / format.dwSamplesPerSec;

            for (uint i = 0; i < format.dwSamplesPerSec - 1; i++)
            {
                // Fill with a simple sine wave at max amplitude
                for (int channel = 0; channel < format.wChannels; channel++)
                {
                    data.dataArray[i * format.wChannels + channel] = (float)Math.Sin(t * i);
                }                        
            }

            break;
    }          
}

Обратите внимание, что вам нужно будет настроить переменные, используемые FloatWaveGenerator, и foreachЦикл сохранения данных должен также записывать правильный тип данных.Я оставляю это как упражнение для вас.:)

...