c#编写网络电话

摘要:语音通话已经是IM的基本功能了,qq,MSN甚至连刚出来的百度HI都自带语音聊天的功能,大家可能觉得很炫,其实大家都是用的windows平台上的API,懂了原理之后自己也可以做,再说了微软也提供了DirectSound的托管互操作程序集,使.net开发人员也很容易的介入到这个领域,甚至你还可以写一个能跑在window mobile上的语音电话,现在好多手机都支持wifi,这样一个简单的wifi电话就由你的手里诞生了。本帖来和大家一起看看如何来做网络电话。

思路:要想做一个网络电话,基本遵循以下步骤
1、一方实时的录音,把模拟信号转换成数字信号;
2、把声音实时压缩;
3、通过网络协议把压缩后的数据传输给接收方;
4、接收方解压缩接受到的音频数据;
5、实时的把接收到的数字信号转换成模拟信号并播放出来。

下面我们来看看每一步面临的挑战及其解决方案。
1、第一步,实时录音,DirectxSound有录音方面的API,托管的类分别是Microsoft.DirectX.DirectSound.CaptureDevicesCollection,Microsoft.DirectX.DirectSound.Capture和Microsoft.DirectX.DirectSound.CaptureBuffer,CaptureDevicesCollection用来枚举本机的可用的录音设备,Capture则表示一个录音设备,CaptureBuffer是用来存放录音数据的缓冲区,我们开始录音后,音频数据会不断的写入到环形的流式缓冲区,然后我们定期从缓冲区中把录音数据取出来返回给上层应用层就可以了。关于环形的流式缓冲区,可以看参考链接部分。
2、声音的压缩是一个很难抉择的步骤,默认的DirectSound只能播放和录制PCM格式(WAV)的音频数据,但这种声音格式特别大。常用的声音压缩格式有h.7231,gsm,amr,h.711等等,各种压缩算法都有自己的码率和适用范围。因为我们做的是互联网的语音电话,不考虑慢速网络和无线连接下的情况,也不用考虑终端设备的CPU能不能支持我们选用的压缩算法,我们做的语音电话双方都是PC机,应该什么解压缩算法都不会引起什么性能上的问题,所以只要网络快一些,选择哪个压缩算法都无所谓了,网上有h.711的压缩算法,我打算就采用这个,他的码率是64Kbps,比PCM的1.544Mbps和2.048Mbps要小的多。然后我们进行了音频数据压缩后,还可以对字节流进行GZIP或者7ZIP压缩,前者用SharpZip,后者7zip的官方有c#的使用代码,大家可以测试一下这两个算法的性能后做出适合自己的决定。关于各种压缩格式的特性可以参考我做的PPT及提供的参考链接。
3、网络电话注重实时性,而把声音从网络上传输就要走IP网络,而IP网络不是一个等时系统,所以我们就要尽量的去模拟实时的语音传输,提到实时,肯定UDP比TCP要实时,因为TCP要保证传输的可靠性,有序性等,而专门用于实时传输有一个应用层协议是RTP协议,这个协议一般就是建立在UDP基础上的,它在每个包头提供了一些序列号、时间戳等信息,但UDP本身并不会使用这些信息,这时候就有一个RTCP协议来用这些信息进行流量控制和拥塞控制,比如说RTCP检测到网络拥挤,会告诉发送方变换一种低码率的语音压缩算法来传输数据。这些大多都需要自己去实现,本文的源码没有去实现这些,关于RTP和RTCP可以参考相关资料或者我做的PPT。
4、每个压缩算法都有相应的解压缩算法,呵呵。
5、播放声音肯定也需要用到DS,也需要用到StreamBuffer,大致流程如下
1)创建一个声音设备Microsoft.DirectX.DirectSound.Device dev = new Microsoft.DirectX.DirectSound.Device();
2)设置协调级别dev.SetCooperativeLevel(this, Microsoft.DirectX.DirectSound.CooperativeLevel.Normal);
3)创建声音格式、缓冲区描述、及辅助缓冲区;
4)给辅助缓冲区设定通知;
5)用声音数据填满缓冲区;
6)播放缓冲区的声音数据,播放到一定的通知点,通知填充线程,填充新的声音数据;
7)循环第6步,直到没有新的声音数据填充到缓冲区。

具体的过程参考PPT或者具体代码。


版权声明:
附件源代码里的CaptureSound,SoundPlayer和CircularBuffer类反编译自随意桌面的代码(注释是我加的),版权归作者所有。
PPT里的图片和一些文字选自一个叫做ch11-DxSound&Input2.ppt的文件,源链接已丢失,还有一些选择一个叫做“SIP之 穿越NAT.ppt”的文件,网上可以搜索到,版权均归原作者所有,源作者要是再引用别人的东西,我就不知道了。

下面看一些具体的代码

用户创建声音格式

 

public   class  DirectSoundManager
ExpandedBlockStart.gifContractedBlock.gif
{
    
public static WaveFormat CreateWaveFormat(int hz, short bits, short channels)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        WaveFormat format 
= new WaveFormat();
        
//声音的格式,通常使用WAVE_FORMAT_PCM来设定,
        
//因为PCM是比较常用的声音格式。
        format.FormatTag = WaveFormatTag.Pcm;
        
//采样率(单位:赫兹)典型值:11025、22050、44100Hz
        format.SamplesPerSecond = hz;
        
//每个采样点数;8-bit或16-bit;
        format.BitsPerSample = bits;
        
//声道的设置,当其值为1时是单声道,为2时是双声道;
        format.Channels = channels;
        
//每个采样点字节数
        format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));
        
//平均传输率,每秒的数据流量
        format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
        
return format;
    }


ContractedSubBlock.gifExpandedSubBlockStart.gif    
属性#region 属性
    
// Properties
    public static WaveFormat DefaultFormat
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
return WaveFormat_8000_8_1;
        }

    }


    
public static WaveFormat WaveFormat_11025_8_1
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
return CreateWaveFormat(0x2b1181);
        }

    }


    
public static WaveFormat WaveFormat_22050_16_2
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
return CreateWaveFormat(0x56220x102);
        }

    }


    
public static WaveFormat WaveFormat_44100_16_2
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
return CreateWaveFormat(0xac440x102);
        }

    }


    
public static WaveFormat WaveFormat_8000_8_1
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
return CreateWaveFormat(0x1f4081);
        }

    }
 
    
#endregion

}


用于播放流式声音
   

 

public   class  SoundPlayer : IDisposable
ExpandedBlockStart.gifContractedBlock.gif
{
ContractedSubBlock.gifExpandedSubBlockStart.gif    
私有成员#region 私有成员
    
private const int MaxLatencyMs = 300;
    
private const int NumberRecordNotifications = 4;
    
private readonly CircularBuffer circularBuffer;
    
private readonly int m_BufferBytes;
    
private readonly bool m_OwnsDevice;
    
private readonly int notifySize;
    
private readonly BufferPositionNotify[] positionNotify;
    
private bool isRunning;
    
private SecondaryBuffer m_Buffer;
    
private Device m_Device;
    
private int nextWriteOffset;
    
private AutoResetEvent notificationEvent;
    
private Notify notify;
    
private Thread notifyThread; 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
构造函数#region 构造函数
    
public SoundPlayer(Control owner, WaveFormat format)
        : 
this(owner, null, format)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
    }


    
public SoundPlayer(Control owner, Device device, WaveFormat format)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        positionNotify 
= new BufferPositionNotify[5];
        notificationEvent 
= null;
        notify 
= null;
        notifyThread 
= null;
        notifySize 
= 0;
        m_Device 
= device;
        
if (m_Device == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            m_Device 
= new Device();
            m_Device.SetCooperativeLevel(owner, CooperativeLevel.Normal);
            m_OwnsDevice 
= true;
        }

        
// 设定通知的大小, 大小为播放一秒钟声音所需要的字节。这里为什么除以8,我不清楚
        notifySize = (1024 > (format.AverageBytesPerSecond / 8)) ? (1024) : ((format.AverageBytesPerSecond / 8));
        notifySize 
= (notifySize - (notifySize % format.BlockAlign));
        m_BufferBytes 
= (notifySize * 4); //整体缓冲区的大小
        BufferDescription desc = new BufferDescription(format);
        
//缓冲区具有控制音量的能力;
        desc.ControlVolume = true;
        
//缓冲区具有控制位置的能力。
        desc.ControlPositionNotify = true;
        
//设置缓冲区能取到当前的播放位置
        desc.CanGetCurrentPosition = true;
        
//缓冲区不具有控制3D音效的能力;
        desc.Control3D = false;
        
//Specifies whether the buffer supports effects processing.
        desc.ControlEffects = false;
        
//缓冲区具有控制频率的能力;
        desc.ControlFrequency = true;
        
//缓冲区具有控制左右声道的能力;
        desc.ControlPan = true;
        
//设置是否使用全局缓存
        desc.GlobalFocus = true;
        
//设置缓冲区大小为整个缓冲区的大小
        desc.BufferBytes = m_BufferBytes;

        
//创建辅助缓冲区
        m_Buffer = new SecondaryBuffer(desc, m_Device);
        
//创建环形缓冲区
        circularBuffer = new CircularBuffer((m_BufferBytes * 10));
        InitNotifications();
        m_Buffer.Play(
0, BufferPlayFlags.Looping);
    }


    
public SoundPlayer(Control owner, int sr, short bps, short ch)
        : 
this(owner, null, DirectSoundManager.CreateWaveFormat(sr, bps, ch))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
    }


    
public SoundPlayer(Control owner, Device device, int sr, short bps, short ch)
        : 
this(owner, device, DirectSoundManager.CreateWaveFormat(sr, bps, ch))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
    }
 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
公开属性#region 公开属性
    
public int BitsPerSample
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return m_Buffer.Format.BitsPerSample; }
    }


    
public int Channels
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return m_Buffer.Format.Channels; }
    }


    
public Device Device
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return m_Device; }
    }


    
public int SamplingRate
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return m_Buffer.Format.SamplesPerSecond; }
    }
 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
IDisposable Members#region IDisposable Members

    
public void Dispose()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        Stop();
        
if (m_Buffer != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            m_Buffer.Dispose();
            m_Buffer 
= null;
        }

        
if (m_OwnsDevice && (m_Device != null))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            m_Device.Dispose();
            m_Device 
= null;
        }

        GC.SuppressFinalize(
this);
    }

    
~SoundPlayer()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        Dispose();
    }


    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
私有方法#region 私有方法
    
private void InitNotifications()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        notifyThread 
= new Thread(NotifyThreadHandler);
        isRunning 
= true;
        notifyThread.IsBackground 
= true;
        notifyThread.Start();
        notificationEvent 
= new AutoResetEvent(false);
        notify 
= new Notify(m_Buffer);
        
//把整个缓冲区分成4个缓冲区片段,每播放4分之一就会给写线程发送一个信号
        for (int i = 0; i < 4; i = (i + 1))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            positionNotify[i].Offset 
= (((notifySize * i) + notifySize) - 1);
            positionNotify[i].EventNotifyHandle 
= notificationEvent.SafeWaitHandle.DangerousGetHandle();
        }

        notify.SetNotificationPositions(positionNotify, 
4);
        nextWriteOffset 
= 0;
    }


    
private void NotifyThreadHandler()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
while (isRunning)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                notificationEvent.WaitOne(
-1true);
                Play();
            }

            
catch (Exception)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
            }

        }

    }


    
private void Play()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
int currentPlayPosition;
                
int currentWritePosition;
                m_Buffer.GetCurrentPosition(
out currentPlayPosition, out currentWritePosition);
                
//得到刚刚播放完的缓冲区片段,这个片段需要用新的数据去填充
                int lockSize = (currentWritePosition - nextWriteOffset);
                
//todo:这里不知道什么时候会发生
                if (lockSize < 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    lockSize 
= (lockSize + m_BufferBytes);
                }

                
//对齐需要填充的缓冲区片段
                lockSize = (lockSize - (lockSize % notifySize));
                
if (0 != lockSize)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    
if (lockSize == m_BufferBytes)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
{
                    }

                    
byte[] data = new byte[lockSize];
                    
if (circularBuffer.Read(data) > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
{
                        m_Buffer.Write(nextWriteOffset, data, LockFlag.None);
                        nextWriteOffset 
= (nextWriteOffset + lockSize);
                        
//如果完整写完一次缓冲区,那么把写数据指针放到缓冲区的最开始,
                        
//因为前面设置了m_Buffer.Play(0, BufferPlayFlags.Looping);
                        
//所以系统在播放缓冲区后会自动重新开始播放缓冲区起始处的声音数据
                        nextWriteOffset = (nextWriteOffset % m_BufferBytes);
                    }

                }

            }

            
catch (Exception)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
            }

        }

        
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
        }

    }
 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
公开方法#region 公开方法
    
public void Stop()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        isRunning 
= false;
        
if (m_Buffer != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            m_Buffer.Stop();
        }

    }


    
public void Write(byte[] data)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            Console.WriteLine(
"播放声音:{0}", data.Length);
            circularBuffer.Write(data);
        }

        
catch (Exception)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
        }

    }
 
    
#endregion

}


 

用户录制声音

 

public   class  CaptureSound
ExpandedBlockStart.gifContractedBlock.gif
{

ContractedSubBlock.gifExpandedSubBlockStart.gif    
私有成员#region 私有成员
    
private const int NumberRecordNotifications = 4;
    
private readonly BufferPositionNotify[] positionNotify;
    
private Capture selectedDevice;
    
private CaptureBuffer buffer;
    
private int captureBufferSize;
    
private string fileName;
    
private bool isRecording;
    
private int nextCaptureOffset;
    
private AutoResetEvent notificationEvent;
    
private Notify notify;
    
private int notifySize;
    
private Thread notifyThread;
    
private long sampleCount;
    
private WaveFormat selectedFormat;
    
private FileStream waveFile;
    
private BinaryWriter writer; 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
构造函数#region 构造函数
    
public CaptureSound()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        isRecording 
= false;
        positionNotify 
= new BufferPositionNotify[5];
        notificationEvent 
= null;
        buffer 
= null;
        fileName 
= string.Empty;
        notify 
= null;
        notifyThread 
= null;
        waveFile 
= null;
        writer 
= null;
        captureBufferSize 
= 0;
        nextCaptureOffset 
= 0;
        sampleCount 
= 0L;
        notifySize 
= 0;
        InitializeDeviceSelector();
        InitializeWaveFormatSelector();
        Initialize();
    }


    
public CaptureSound(Capture device)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        isRecording 
= false;
        positionNotify 
= new BufferPositionNotify[5];
        notificationEvent 
= null;
        buffer 
= null;
        fileName 
= string.Empty;
        notify 
= null;
        notifyThread 
= null;
        waveFile 
= null;
        writer 
= null;
        captureBufferSize 
= 0;
        nextCaptureOffset 
= 0;
        sampleCount 
= 0L;
        notifySize 
= 0;
        selectedDevice 
= device;
        InitializeWaveFormatSelector();
        Initialize();
    }


    
public CaptureSound(WaveFormat waveFormat)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        isRecording 
= false;
        positionNotify 
= new BufferPositionNotify[5];
        notificationEvent 
= null;
        buffer 
= null;
        fileName 
= string.Empty;
        notify 
= null;
        notifyThread 
= null;
        waveFile 
= null;
        writer 
= null;
        captureBufferSize 
= 0;
        nextCaptureOffset 
= 0;
        sampleCount 
= 0L;
        notifySize 
= 0;
        selectedFormat 
= waveFormat;
        InitializeDeviceSelector();
        Initialize();
    }


    
public CaptureSound(Capture device, WaveFormat waveFormat)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        isRecording 
= false;
        positionNotify 
= new BufferPositionNotify[5];
        notificationEvent 
= null;
        buffer 
= null;
        fileName 
= string.Empty;
        notify 
= null;
        notifyThread 
= null;
        waveFile 
= null;
        writer 
= null;
        captureBufferSize 
= 0;
        nextCaptureOffset 
= 0;
        sampleCount 
= 0L;
        notifySize 
= 0;
        selectedDevice 
= device;
        selectedFormat 
= waveFormat;
        Initialize();
    }
 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
公开属性#region 公开属性
    
public int BufferBytes
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return captureBufferSize; }
    }


    
public string FileName
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return fileName; }
        
set
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            fileName 
= value;
            CreateRIFF();
        }

    }


    
public long SampleCount
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return sampleCount; }
    }


    
public WaveFormat SelectedFormat
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
ExpandedSubBlockStart.gifContractedSubBlock.gif        
get return selectedFormat; }
    }
 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
公开事件#region 公开事件
    
public event DirectSoundBufferDataEventHandler BufferData; 
    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
私有方法#region 私有方法
    
private void CreateCaptureBuffer()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        CaptureBufferDescription desc 
= new CaptureBufferDescription();
        
if (null != notify)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            notify.Dispose();
            notify 
= null;
        }

        
if (null != buffer)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            buffer.Dispose();
            buffer 
= null;
        }

        
if (0 != selectedFormat.Channels)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            notifySize 
= (1024 > (selectedFormat.AverageBytesPerSecond / 8))
                             
? (1024)
                             :
                                 ((selectedFormat.AverageBytesPerSecond 
/ 8));
            notifySize 
= (notifySize - (notifySize % selectedFormat.BlockAlign));
            captureBufferSize 
= (notifySize * 4);
            desc.BufferBytes 
= captureBufferSize;
            selectedFormat.FormatTag 
= WaveFormatTag.Pcm;
            desc.Format 
= selectedFormat;
            buffer 
= new CaptureBuffer(desc, selectedDevice);
            nextCaptureOffset 
= 0;
            InitNotifications();
        }

    }


    
private void CreateRIFF()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        waveFile 
= new FileStream(FileName, FileMode.Create);
        writer 
= new BinaryWriter(waveFile);
ExpandedSubBlockStart.gifContractedSubBlock.gif        
char[] chArray = new char[] 'R''I''F''F' };
ExpandedSubBlockStart.gifContractedSubBlock.gif        
char[] chArray2 = new char[] 'W''A''V''E' };
ExpandedSubBlockStart.gifContractedSubBlock.gif        
char[] chArray3 = new char[] 'f''m''t'' ' };
ExpandedSubBlockStart.gifContractedSubBlock.gif        
char[] chArray4 = new char[] 'd''a''t''a' };
        
short num = 1;
        
int num2 = 0x10;
        
int num3 = 0;
        
short num4 = 0;
        
if ((8 == selectedFormat.BitsPerSample) && (1 == selectedFormat.Channels))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            num4 
= 1;
        }

        
else if (((8 == selectedFormat.BitsPerSample) && (2 == selectedFormat.Channels)) ||
                 ((
0x10 == selectedFormat.BitsPerSample) && (1 == selectedFormat.Channels)))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            num4 
= 2;
        }

        
else if ((0x10 == selectedFormat.BitsPerSample) && (2 == selectedFormat.Channels))
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            num4 
= 4;
        }

        writer.Write(chArray);
        writer.Write(num3);
        writer.Write(chArray2);
        writer.Write(chArray3);
        writer.Write(num2);
        writer.Write(num);
        writer.Write(selectedFormat.Channels);
        writer.Write(selectedFormat.SamplesPerSecond);
        writer.Write(selectedFormat.AverageBytesPerSecond);
        writer.Write(num4);
        writer.Write(selectedFormat.BitsPerSample);
        writer.Write(chArray4);
        writer.Write(
0);
    }


    
private void Initialize()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        CreateCaptureBuffer();
    }


    
private void InitializeDeviceSelector()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{

        CaptureDevicesCollection devices 
= new CaptureDevicesCollection();  // 枚举音频捕捉设备 

        
if (devices.Count > 0)
            selectedDevice 
= new Capture(devices[0].DriverGuid);
        
else
            
throw new ArgumentException("无法初始化声音设备");
    }


    
private void InitializeWaveFormatSelector()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
if (selectedDevice == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
throw new ArgumentException("尚未設定音訊裝置,無法選擇輸出格式。");
        }

        selectedFormat 
= DirectSoundManager.DefaultFormat;
    }


    
private void InitNotifications()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
if (null == buffer)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
throw new NullReferenceException();
        }

        
if (null == notifyThread)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            isRecording 
= true;
            notifyThread 
= new Thread(WaitThread);
            notifyThread.IsBackground 
= true;
            notifyThread.Start();
            notificationEvent 
= new AutoResetEvent(false);
        }

        
for (int i = 0; i < 4; i++)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            positionNotify[i].Offset 
= (((notifySize * i) + notifySize) - 1);
            positionNotify[i].EventNotifyHandle 
= notificationEvent.SafeWaitHandle.DangerousGetHandle();
        }

        notify 
= new Notify(buffer);
        notify.SetNotificationPositions(positionNotify, 
4);
    }


    
private void OnBufferData(object sender, DirectSoundBufferDataEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
if (BufferData != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            BufferData(sender, e);
        }

    }

    
private void RecordCapturedData()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
byte[] data = null;
        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                
int currentPlayPosition;
                
int currentWritePosition;
                buffer.GetCurrentPosition(
out currentWritePosition, out currentPlayPosition);
                
int lockSize = (currentPlayPosition - nextCaptureOffset);
                
if (lockSize < 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
                    lockSize 
= (lockSize + captureBufferSize);
                }

                lockSize 
= (lockSize - (lockSize % notifySize));
                
if (0 != lockSize)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
{
ExpandedSubBlockStart.gifContractedSubBlock.gif                    data 
= (byte[])buffer.Read(nextCaptureOffset, typeof(byte), LockFlag.None, new int[] { lockSize });
                    OnBufferData(
thisnew DirectSoundBufferDataEventArgs(data));
                    
if (writer != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
{
                        writer.Write(data, 
0, data.Length);
                    }

                    sampleCount 
= (sampleCount + data.Length);
                    nextCaptureOffset 
= (nextCaptureOffset + data.Length);
                    nextCaptureOffset 
= (nextCaptureOffset % captureBufferSize);

                }

            }

            
catch (Exception)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
            }

        }

        
finally
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            data 
= null;
        }

    }


    
private void WaitThread()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
while (isRecording)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            
try
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                notificationEvent.WaitOne(
-1true);
                RecordCapturedData();
            }

            
catch (Exception)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
            }

        }

    }

    
private void StartOrStopRecord(bool StartRecording)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        
if (StartRecording)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            isRecording 
= true;
            CreateCaptureBuffer();
            buffer.Start(
true);
        }

        
else
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            isRecording 
= false;
            buffer.Stop();
            RecordCapturedData();
            
if (writer != null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
{
                writer.Seek(
4, SeekOrigin.Begin);
                writer.Write(((
int)(sampleCount + 0x24L)));
                writer.Seek(
40, SeekOrigin.Begin);
                writer.Write(sampleCount);
                writer.Close();
                writer 
= null;
                waveFile 
= null;
            }

        }

    }

    
#endregion


ContractedSubBlock.gifExpandedSubBlockStart.gif    
公开方法#region 公开方法
    
public void Pause()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        buffer.Stop();
    }


    
public void Resume()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        buffer.Start(
true);
    }


    
public void Start()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        StartOrStopRecord(
true);
    }


    
public void Stop()
ExpandedSubBlockStart.gifContractedSubBlock.gif    
{
        StartOrStopRecord(
false);
        notifyThread 
= null;
        nextCaptureOffset 
= 0;
        sampleCount 
= 0L;
    }
 
    
#endregion

}


 

程序下载地址如下(自己反射看源码,因为程序只是用于演示,所以考虑很不周全,不足以效仿)
http://files.cnblogs.com/onlytiancai/wawaim.zip
PPT下载地址如下
http://files.cnblogs.com/onlytiancai/p2p语音.zip

参考链接如下:
ch11-DxSound&Input2.ppt:建立DirectSound 声音的播放与控制 使用3D音效
SIP之 穿越NAT.ppt
DirectX技术实现视频会议中的音频通信
http://www.ctiforum.com/forum/2008/03/forum08_0357.htm
C#中使用DirectSound录音
http://blog.donews.com/uplook/archive/2005/12/14/657145.aspx
在C#下利用DirectSound实现声音播放
http://www.cnblogs.com/yangbeibei/archive/2006/08/30/490270.html
隨意桌面,數位溝通
http://cuteofdragon.blogspot.com/2007/05/blog-post_9694.html
用DirectX Audio和DirectShow播放声音和音乐
http://www.cppblog.com/lovedday/archive/2007/09/24/32815.html

转载于:https://www.cnblogs.com/dfsxh/archive/2008/12/16/1355886.html

[DllImport("phone.dll")] private static extern IntPtr PhoneMakeCall(ref PhoneMakeCallInfo ppmci); /// /// Dials the specified phone number. /// /// Phone number to dial. public static void MakeCall(string PhoneNumber) { MakeCall(PhoneNumber, false); } /// /// Dials the specified phone number. /// /// Phone number to dial. /// Prompts the user before the call is placed. unsafe public static void MakeCall(string PhoneNumber, bool PromptBeforeCall) { IntPtr res; PhoneNumber += '\0'; char[] cPhoneNumber = PhoneNumber.ToCharArray(); fixed (char* pAddr = cPhoneNumber) { PhoneMakeCallInfo info = new PhoneMakeCallInfo(); info.cbSize = (IntPtr)Marshal.SizeOf(info); info.pszDestAddress = (IntPtr)pAddr; if (PromptBeforeCall) { info.dwFlags = (IntPtr)PMCF_PROMPTBEFORECALLING; } else { info.dwFlags = (IntPtr)PMCF_DEFAULT; } res = PhoneMakeCall(ref info); } } } /// /// Reads information from the Subscriber Identity Module (SIM) /// public class Sim { private static long SERVICE_PROVIDER = 0x00006F46; [StructLayout(LayoutKind.Sequential)] private struct SimRecord { public IntPtr cbSize; public IntPtr dwParams; public IntPtr dwRecordType; public IntPtr dwItemCount; public IntPtr dwSize; } [DllImport("sms.dll")] private static extern IntPtr SmsGetPhoneNumber(IntPtr psmsaAddress); [DllImport("cellcore.dll")] private static extern IntPtr SimInitialize(IntPtr dwFlags, IntPtr lpfnCallBack, IntPtr dwParam, out IntPtr lphSim); [DllImport("cellcore.dll")] private static extern IntPtr SimGetRecordInfo(IntPtr hSim, IntPtr dwAddress, ref SimRecord lpSimRecordInfo); [DllImport("cellcore.dll")] private static extern IntPtr SimReadRecord(IntPtr hSim, IntPtr dwAddress, IntPtr dwRecordType, IntPtr dwIndex, byte[] lpData, IntPtr dwBufferSize, ref IntPtr lpdwBytesRead); [DllImport("cellcore.dll")] private static extern IntPtr SimDeinitialize(IntPtr hSim); /// /// Gets the phone number from the SIM. /// /// PhoneAddress structure with phone number. unsafe public static PhoneAddress GetPhoneNumber() { PhoneAddress phoneaddr = new PhoneAddress(); Byte[] buffer = new Byte[516]; fixed (byte* pAddr = buffer) { IntPtr res = SmsGetPhoneNumber((IntPtr)pAddr); if (res != IntPtr.Zero) throw new Exception("Could not get phone number from SIM"); byte* pCurrent = pAddr; phoneaddr.AddressType = (AddressType)Marshal.ReadInt32((IntPtr)pCurrent); pCurrent += Marshal.SizeOf(phoneaddr.AddressType); phoneaddr.Address = Marshal.PtrToStringUni((IntPtr)pCurrent); } return phoneaddr; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值