C#创建24/32位声音wav文件
C# create 24/32bit sound wav file
我在 Microsoft Developer 博客上关注此教程:https://blogs.msdn.microsoft.com/dawate/2009/06/24/intro-to-audio-programming-part-3-synthesizing-simple-wave-audio-using-c/
教程名为 "Intro to Audio Programming",我按照所有步骤操作,但可能遗漏了什么。
这是从WaveFormatChunk
class中摘取的配置片在他的谐音法中:
sChunkID = "fmt ";
dwChunkSize = 16;
wFormatTag = 1;
wChannels = 2;
dwSamplesPerSec = 44100;
wBitsPerSample = 16;
wBlockAlign = (ushort)(wChannels * (wBitsPerSample / 8));
dwAvgBytesPerSec = dwSamplesPerSec * wBlockAlign;
wBitsPerSample
设置生成函数的位深度,到现在还很简单。
然后,运行 程序,都在使用此设置。在 16 位/44.1ksample 下生成振幅为 32760 的 1Hz 频率,结果如下:
1Hz 16bit 44.1k
这显然是正确的输出。
现在引用一下:
we use an array of shorts because we have 16-bit samples as specified in the format block. If you want to change to 8-bit audio, use an array of bytes. If you want to use 32-bit audio, use an array of floats.
在 WaveDataChunk
class
中谈论 shortArray
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;
这是结果:
1Hz 32bit 44.1k
实际上,频率翻倍,只写了一半的时间。我做错了什么??我要做什么?
当您使用 IEEE 浮点数时,范围应为 -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 循环也必须写入正确的数据类型。我把这个留给你作为练习。 :)
我在 Microsoft Developer 博客上关注此教程:https://blogs.msdn.microsoft.com/dawate/2009/06/24/intro-to-audio-programming-part-3-synthesizing-simple-wave-audio-using-c/
教程名为 "Intro to Audio Programming",我按照所有步骤操作,但可能遗漏了什么。
这是从WaveFormatChunk
class中摘取的配置片在他的谐音法中:
sChunkID = "fmt ";
dwChunkSize = 16;
wFormatTag = 1;
wChannels = 2;
dwSamplesPerSec = 44100;
wBitsPerSample = 16;
wBlockAlign = (ushort)(wChannels * (wBitsPerSample / 8));
dwAvgBytesPerSec = dwSamplesPerSec * wBlockAlign;
wBitsPerSample
设置生成函数的位深度,到现在还很简单。
然后,运行 程序,都在使用此设置。在 16 位/44.1ksample 下生成振幅为 32760 的 1Hz 频率,结果如下:
1Hz 16bit 44.1k
这显然是正确的输出。
现在引用一下:
we use an array of shorts because we have 16-bit samples as specified in the format block. If you want to change to 8-bit audio, use an array of bytes. If you want to use 32-bit audio, use an array of floats.
在 WaveDataChunk
class
shortArray
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;
这是结果: 1Hz 32bit 44.1k
实际上,频率翻倍,只写了一半的时间。我做错了什么??我要做什么?
当您使用 IEEE 浮点数时,范围应为 -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 循环也必须写入正确的数据类型。我把这个留给你作为练习。 :)