最近由于着手一个无线电接受机信号处理的工作,需要处理解调模式下传递的PCM数据,绘制波形并播放声音,特此记录。
模拟PCM数据,保存到本地
private void button1_Click(object sender, EventArgs e)
{
string fileName = "123.pcm";
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
//StreamWriter sw = new StreamWriter(fs);
//BinaryWriter bw = new BinaryWriter(fs);
Random r = new Random();
byte[] pcmData = new byte[2048];
for (int j = 0; j < 1024; j++)
{
//for (int i = 0; i < pcmData.Length; i++)
r.NextBytes(pcmData);
fs.Write(pcmData, 0, pcmData.Length);
}
fs.Close();
}
读取模拟的PCM数据创建WAV文件,并保存到本地
private void button2_Click(object sender, EventArgs e)
{
InitialStruct();
if (readPcm("123.pcm") && InitHeader())
{
string wavFile = Path.GetRandomFileName() + ".wav";
WriteFile(wavFile, databuff);
MessageBox.Show(wavFile);
//lstMessage.Items.Add("WAVA文件转换成功!" + System.DateTime.Now.ToString());
}
}
///
/// ERROR MESSAGE
///
const string ERRFILENOTEXITS = "File is Not Exits.";
const string ERRFILEISNOTWAVE = "File is not Wava.";
///
/// Wave Hander information
///
struct HeaderType
{
public byte[] riff;
public uint file_len;
public byte[] wave;
public byte[] fmt;
public uint NI1;
public ushort format_type;
public ushort Channels;
public uint frequency;
public uint trans_speed;
public ushort dataBlock;
public ushort sample_bits;
public byte[] data;
public uint wav_len;
}
private HeaderType wavHander; //定义一个头结构体
private byte[] buff = new byte[44]; //header byte
private byte[] databuff; //data byte
///
/// 初始化结构体中的数组长度,分配内存
///
private void InitialStruct()
{
wavHander.riff = new byte[4];//RIFF
wavHander.wave = new byte[4];//WAVE
wavHander.fmt = new byte[4];//fmt
wavHander.data = new byte[4];//data
}
///
/// 读取PCM中数据,
///
/// 文件路径
/// 读取成功返回真
private bool readPcm(string filepath)
{
String fileName = filepath;//临时保存文件名
if (File.Exists(fileName) == false)//文件不存在
{
throw new Exception(ERRFILENOTEXITS);
}
//自读方式打开
FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None);
if (file == null)//打开成功
{
file.Close();//关闭文件
throw new Exception(ERRFILEISNOTWAVE);
}
int filelen = (int)file.Length;//获取文件长度
databuff = new byte[filelen + 44];//分配 内存
file.Read(databuff, 44, filelen);//读取文件,保存在内存中
file.Close();//关闭文件
return true;
}
///
/// 为PCM文件构建文件头,准备转换为WAV文件
///
/// 构建成功返回真
private bool InitHeader()
{
wavHander.riff = Encoding.ASCII.GetBytes("RIFF");
wavHander.file_len = (uint)(databuff.Length);
wavHander.wave = Encoding.ASCII.GetBytes("WAVE");
wavHander.fmt = Encoding.ASCII.GetBytes("fmt ");
wavHander.NI1 = 0x10;
wavHander.format_type = 0x01;
wavHander.Channels = 0x01;
wavHander.frequency = 0x1F40;
wavHander.trans_speed = 0x3E80;
wavHander.dataBlock = 0x02;
wavHander.sample_bits = 0x10;
wavHander.data = Encoding.ASCII.GetBytes("data");
wavHander.wav_len = (uint)(databuff.Length - 44);
byte[] byt2;//临时变量 ,保存2位的整数
byte[] byt4;//临时变量, 保存4位的整数
Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(wavHander.riff), 0, 4, databuff, 0);
byt4 = BitConverter.GetBytes(wavHander.file_len);
Array.Copy(byt4, 0, databuff, 4, 4);
Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(wavHander.wave), 0, 4, databuff, 8);
Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(wavHander.fmt), 0, 4, databuff, 12);
byt4 = BitConverter.GetBytes(wavHander.NI1);
Array.Copy(byt4, 0, databuff, 16, 4);
byt2 = BitConverter.GetBytes(wavHander.format_type);
Array.Copy(byt2, 0, databuff, 20, 2);
byt2 = BitConverter.GetBytes(wavHander.Channels);
Array.Copy(byt2, 0, databuff, 22, 2);
byt4 = BitConverter.GetBytes(wavHander.frequency);
Array.Copy(byt4, 0, databuff, 24, 4);
byt4 = BitConverter.GetBytes(wavHander.trans_speed);
Array.Copy(byt4, 0, databuff, 28, 4);
byt2 = BitConverter.GetBytes(wavHander.dataBlock);
Array.Copy(byt2, 0, databuff, 32, 2);
byt2 = BitConverter.GetBytes(wavHander.sample_bits);
Array.Copy(byt2, 0, databuff, 34, 2);
Encoding.ASCII.GetBytes(Encoding.ASCII.GetString(wavHander.data), 0, 4, databuff, 36);
byt4 = BitConverter.GetBytes(wavHander.wav_len);
Array.Copy(byt4, 0, databuff, 40, 4);
return true;
}
///
/// 写文件操作
///
/// 文件路径
/// 文件数据
private void WriteFile(string filename, byte[] pbuff)
{
if (File.Exists(filename) == true)
File.Delete(filename);
FileStream sw = File.OpenWrite(filename);
if (pbuff != null && sw != null)
{
sw.Write(pbuff, 0, pbuff.Length);
sw.Close();
}
}
使用NAudio 播放PCM数据
WaveOut waveOut; //播放器
BufferedWaveProvider bufferedWaveProvider; //5s缓存区
private void button3_Click(object sender, EventArgs e)
{
waveOut = new WaveOut();
WaveFormat wf = new WaveFormat(8000, 16, 1);
WaveFormat alawFormat = WaveFormat.CreateALawFormat(8000, 1);
// ALawChatCodec alaw = new ALawChatCodec();
bufferedWaveProvider = new BufferedWaveProvider(wf);
bufferedWaveProvider.DiscardonBufferOverflow = true;
waveOut.Init(bufferedWaveProvider);
waveOut.Play();
readPcm("123.pcm");
bufferedWaveProvider.AddSamples(databuff, 0, databuff.Length);
}
代码中的bufferedWaveProvider.DiscardonBufferOverflow = true;设置必须有,不然会报错:buffer null这个问题困扰了我很长时间
参考文章:c# pcm - jasonlai2016 - 博客园
stream - c# - NAudio buffer full exception - Stack Overflow
PCM详解资料参考:PCM数据格式介绍_SuperLi-CSDN博客_pcm数据格式
PCM音量控制 - 剑痴乎



