编写适当的Wave文件(译文)
By S.F.
本文链接 https://www.kyfws.com/news/writing-a-proper-wave-file/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 3 分钟阅读 - 1358 个词 阅读量 0编写适当的Wave文件(译文)
原文地址:https://www.codeproject.com/Articles/129173/Writing-a-Proper-Wave-File
原文作者:Joel Ivory Johnson
译文由本站翻译
前言
在Windows Phone 7中播放从麦克风录制的数据
下载源 目前,我在Windows Phone 7论坛中看到的一个重复出现的问题之一是播放从麦克风录制的数据.通常,开发人员会将他们从麦克风接收到的声音字节写到文件中,然后尝试导出它们以进行播放或使用电话上的媒体类进行播放,只是发现无法处理该文件.在今天的午休时间里,我有机会将一些东西融合在一起,我认为这将为那些开发人员指明正确的方向.
为什么不播放文件
该文件将无法播放,因为没有给它提供任何组件或软件来了解有关该文件的任何信息.如果您从麦克风录制并将原始字节转储到文件中,则您不写的内容包括采样率,每个采样的位数,文件格式等等.您需要在文件前添加所有这些内容,以使媒体类可以使用它.经过快速的Bing搜索,我在这里找到了所需标头的描述.使用该工具,我整理了一个快速的桌面应用程序,该应用程序可生成可播放的wave文件.我针对的是台式机,因为我使用的计算机没有电话开发人员工具.但是,台式机和手机上的代码几乎相同.唯一的区别在于文件的创建.当我直接创建文件流时,您将在隔离存储中创建流.
模拟音频数据
我需要一些数据写入我的文件.根据我的喜好,我创建了一个函数,该函数将用Sine函数的输出填充字节数组.作为参数,它采用采样率,我想要播放声音的时间长度,波的频率以及幅度(0为最低幅度,1为最大幅度),并以字节数组形式返回数据.您将使用记录中的字节填充数组.我用来执行此操作的代码如下:
public static byte[] CreateSinWave(
int sampleRate,
double frequency,
TimeSpan length,
double magnitude
)
{
int sampleCount = (int)(((double)sampleRate) * length.TotalSeconds);
short[] tempBuffer = new short[sampleCount];
byte[] retVal = new byte[sampleCount*2];
double step = Math.PI*2.0d/frequency;
double current = 0;
for(int i=0;i<tempBuffer.Length;++i)
{
tempBuffer[i] = (short)(Math.Sin(current) * magnitude * ((double)short.MaxValue));
current += step;
}
Buffer.BlockCopy(tempBuffer,0,retVal,0,retVal.Length);
return retVal;
}
填充波头
有更好的方法可以做到这一点,更好的方法.但是我只是想在短时间内创造出令人满意的东西. 在描述波形标题的图表上,我写了字面字节或计算值,其中计算值基于采样率,通道数和其他一些因素.关于它,没有太多要说的,但是代码如下:
static byte[] RIFF_HEADER = new byte[] { 0x52, 0x49, 0x46, 0x46 };
static byte[] FORMAT_WAVE = new byte[] { 0x57, 0x41, 0x56, 0x45 };
static byte[] FORMAT_TAG = new byte[] { 0x66, 0x6d, 0x74, 0x20 };
static byte[] AUDIO_FORMAT = new byte[] {0x01, 0x00};
static byte[] SUBCHUNK_ID = new byte[] { 0x64, 0x61, 0x74, 0x61 };
private const int BYTES_PER_SAMPLE = 2;
public static void WriteHeader(
System.IO.Stream targetStream,
int byteStreamSize,
int channelCount,
int sampleRate)
{
int byteRate = sampleRate*channelCount*BYTES_PER_SAMPLE;
int blockAlign = channelCount*BYTES_PER_SAMPLE;
targetStream.Write(RIFF_HEADER,0,RIFF_HEADER.Length);
targetStream.Write(PackageInt(byteStreamSize+42, 4), 0, 4);
targetStream.Write(FORMAT_WAVE, 0, FORMAT_WAVE.Length);
targetStream.Write(FORMAT_TAG, 0, FORMAT_TAG.Length);
targetStream.Write(PackageInt(16,4), 0, 4);//Subchunk1Size
targetStream.Write(AUDIO_FORMAT, 0, AUDIO_FORMAT.Length);//AudioFormat
targetStream.Write(PackageInt(channelCount, 2), 0, 2);
targetStream.Write(PackageInt(sampleRate, 4), 0, 4);
targetStream.Write(PackageInt(byteRate, 4), 0, 4);
targetStream.Write(PackageInt(blockAlign, 2), 0, 2);
targetStream.Write(PackageInt(BYTES_PER_SAMPLE*8), 0, 2);
//targetStream.Write(PackageInt(0,2), 0, 2);//Extra param size
targetStream.Write(SUBCHUNK_ID, 0, SUBCHUNK_ID.Length);
targetStream.Write(PackageInt(byteStreamSize, 4), 0, 4);
}
static byte[] PackageInt(int source, int length=2)
{
if((length!=2)&&(length!=4))
throw new ArgumentException("length must be either 2 or 4", "length");
var retVal = new byte[length];
retVal[0] = (byte)(source & 0xFF);
retVal[1] = (byte)((source >> 8) & 0xFF);
if (length == 4)
{
retVal[2] = (byte) ((source >> 0x10) & 0xFF);
retVal[3] = (byte) ((source >> 0x18) & 0xFF);
}
return retVal;
}
这就是您几乎需要知道的所有内容.为了使用该代码,我编写了一个简单的控制台模式程序.
static void Main(string[] args)
{
var soundData = WaveHeaderWriter.CreateSinWave(44000, 120, TimeSpan.FromSeconds(60), 1d);
using(FileStream fs = new FileStream("MySound2.wav", FileMode.Create))
{
WaveHeaderWriter.WriteHeader(fs, soundData.Length, 1, 44100);
fs.Write(soundData,0,soundData.Length);
fs.Close();
}
}
我在Audacity中打开了结果输出,结果是我所期望的.
当然,作为最终测试,我双击了该文件.它在Windows Media Player中打开并播放了正弦波. 这样就可以使用该程序了!如果有机会,我将尝试在Windows Phone 7中创建该版本.那些拥有WPDT而没有完整版Visual Studio的用户将无法直接编译该程序.但是,如果要运行二进制文件,则将其包含在源代码中. CodeProject
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C# Mobile Dev Intermediate Windows-Phone-7 新闻 翻译