65.9K
CodeProject 正在变化。 阅读更多。
Home

播放或捕获音频。以组播(RTP)方式发送和接收

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (63投票s)

2012 年 5 月 31 日

CPOL

4分钟阅读

viewsIcon

315320

downloadIcon

38969

播放、录制和捕获音频。读写和流式传输 WAV 文件。发送和接收 Ulaw RTP 流。使用 WMM API 的 WaveIn WaveOut 函数。

引言

在 .NET 中使用声音 API 并不方便,因为类库本身并不支持。所以我决定使用 PInvoke 和本地 WMM API(Windows Multi Media)来编写我自己的小型音频程序集WinSound.dll。借助这个库,您可以轻松地播放或捕获音频。此外,我还编写了一些网络程序集,通过组播流式传输和播放音频数据。

可选 C++

此外,我还实现了该项目声音部分的类似 C++ 版本,作为名为 WinSoundServer 的 COM 服务器。项目中还包含一个用于 Visual Studio 2012 的小型 WinSoundServerTester.sln,它在示例中演示了录制和播放方法。我本来不打算发布它,但也许有人会觉得它有用。您必须先编译 WinSoundServer 项目,才能创建 vc100.tlbWinSoundServer.dll。在启动测试应用程序之前,您必须使用项目中包含的 reg.bat 文件注册 COM 服务器。这将注册 WinSoundServer.dll。之后,测试应用程序必须通过 import 语句引用 typelibrary,以便您可以访问所有接口和类(就像在 C# 中添加引用一样)。

#import "WinSoundServer\\vc100.tlb" no_namespace

背景

WMM 的 WaveOutWaveIn 函数非常棘手,因为您需要了解一些关于如何处理内部缓冲区和句柄的特殊功能。例如,您不能在等待所有缓冲区完成之前停止录制。您不允许在回调函数中停留太长时间。您必须在托管代码和非托管代码之间切换。Repeater 是最敏感的,因为您必须始终平衡录制和播放缓冲区。

Using the Code

用作 DLL 的程序集

用于通用声音操作

  • WinSound.dll

用于网络操作

  • TCPClient.dll
  • TCPServer.dll
  • MulticastReceiver.dll
  • MulticastSender.dll

示例项目

  • MulticastPlayer(播放到声卡的 RTP 组播)
  • MulticastStreamer(从声卡发送 RTP 组播)
  • Player Tester(播放或录制 WAV 文件)
  • RepeaterTester(从声卡到声卡播放)

所有这些的核心是 WinSound.dll。所有示例项目都需要此 DLL(添加为引用)。

使用声音录音机

请注意,buffersizebuffercount 必须针对您的 Sounddevice 和使用的 SamplesPerSecond 进行优化。

//Create a Recorder
WinSound.Recorder recorder = new WinSound.Recorder();
//Add DataRecording and RecordingStopped Event Functions
recorder.DataRecorded += new WinSound.Recorder.DelegateDataRecorded(OnDataRecorded);
recorder.RecordingStopped += new WinSound.Recorder.DelegateStopped(OnRecordingStopped);
//Start capturing (44100 SamplesPerSecond, 16 BitsPerSample, 2 Channels, 8 * 4096 Byte Buffers)
recorder.Start("Line 1 (Virtual Audio Cable)", 44100, 16, 2, 8, 4096);
//Check if started
bool isStarted = recorder.Started;
//Stop recording
recorder.Stop();

//Called, when datas are incoming
private void OnDataRecorded(Byte[] data)
{
    
}

//Called, when recording has stopped
private void OnRecordingStopped()
{
    
}

使用声音播放器

请注意,buffercount 必须针对您的 Sounddevice 和使用的 SamplesPerSecond 进行优化。

//Create a SoundPlayer
WinSound.Player player = new WinSound.Player();
//Add Player Stopped and Player Closed Events
player.PlayerStopped += new WinSound.Player.DelegateStopped(OnPlayerStopped);
player.PlayerClosed += new WinSound.Player.DelegateStopped(OnPlayerClosed);
//Open a SoundDevice (44100 SamplesPerSecond, 16 BitsPerSample, 2 Channels, 8 Buffers)
player.Open("Realtek", 44100, 16, 2, 8);
//Check if open
bool isOpen = player.Opened;          
//Get Sound Datas as linear bytes (maybe from Wav-File)
Byte[] data = GetDatas();
//Play linear datas to SoundDevice
player.PlayData(data, false);
//Check if playing
bool isPlaying = player.Playing
//Start pause
player.StartPause();
//Check if paused
bool isPaused = player.Paused;
//End pause
player.EndPause();
//Close Player
player.Stop();
                    
//Called, when player is stopped 
private void OnPlayerStopped()
{

}

//Called, when player is closed (WaveOut closed)
private void OnPlayerClosed()
{

}  

线性字节

您从声卡读取或写入的线性数据字节取决于 BitsPerSample 和通道数。每个“数据包”的解释方式如图所示。

播放器(录音机)测试器

此示例使用 WinSound.dll 来播放或录制 WAV 文件。SamplesPerSecondBitsPerSampleChannels 仅在录制模式下使用。

//Create a SoundPlayer
WinSound.Player playerOne = new WinSound.Player();
//Add callback functions for stop and close events (optional)
playerOne.PlayerClosed += new WinSound.Player.DelegateStopped(OnPlayerClosed);
playerOne.PlayerStopped += new WinSound.Player.DelegateStopped(OnPlayerStopped);
//Play a Wav File on a specific Sounddevice
playerOne.PlayFile(@"C:\Record.wav", "Realtek");   
//Close the player
bool hr = playerOne.Close(); 

//Read Wave-Header and payload data from Wav-File
WinSound.WaveFileHeader header = WinSound.WaveFile.Read(@"C:\Record.wav");
//Get the payload data size
uint dataSize = header.DATASize;
//Get the payload data
Byte[] data = header.Payload;

//Write payload data in a new Wav-File 
WinSound.WaveFile.Create(@"C:\Record.wav", 44100, 16, 2, data);
//Add payload data in an existing Wav-File (Append)
WinSound.WaveFile.AppendData(@"C:\Record.wav", data);  

Repeater 测试器

此示例将声音数据从一个声卡(WaveIn)录制到另一个(WaveOut)。每个声卡都有其特殊的功能,因此您必须更改 BufferCountBufferSize 以获得最佳性能。它在 Windows-XP、Vista 或 Windows 7 操作系统上也有所不同。

394890/2.png

//Create the Repeater
WinSound.Repeater repeaterOne = new WinSound.Repeater(); 
//Start the Repeater
repeaterOne.Start("Line 6", "Realtek", samplesPerSecond, bitsPerSample, 
                   channels, bufferCount, bufferSize);
//Stop the Repeater 
repeaterOne.Stop();

组播流媒体

此示例将 ULAW RTP 组播数据从声卡或 WAV 文件流式传输。使用声卡流式传输时,可以将其与“Time Sync”复选框同步。Buffer Count 是从声卡捕获时使用的缓冲区数量(标准值为 8)。您可以使用 Multicast Player 播放此组播。您还可以使用 VLC 媒体播放器进行测试(在 VLC 中使用 Samples Per Second = 8000 和 BitsPerSample = 16)。

//Create a  Multicast Sender and a Sound-Device Recorder
NF.MulticastSender m_MulticastSender = new NF.MulticastSender(Address, Port, TTL);
WinSound.Recorder m_Recorder = new WinSound.Recorder();
//Define a callback function for receiving datas from soundcard
m_Recorder.DataRecorded += new WinSound.Recorder.DelegateDataRecorded(OnDataReceivedFromSoundcard);
//Start Recorder 
m_Recorder.Start
(SoundDeviceName, SamplesPerSecond, BitsPerSample, ChannelCount, BufferCount, BufferSize) 
//In callback function, get the linear datas from soundcard and translate to MuLaw
Byte[] mulawData = WinSound.Utils.LinearToMulaw(linearData, Config.BitsPerSample, Config.Channels);
//Create the RTP Header 
Byte[] rtpBytes = RTPHeader.ToBytes();
//Combine RTP Header and mulawData
Byte[] bytes = new Byte[mulawData.Length + WinSound.RTPPacket.MinHeaderLength];
Array.Copy(rtpBytes, 0, bytes, 0, WinSound.RTPPacket.MinHeaderLength);
Array.Copy(mulawData, 0, bytes, WinSound.RTPPacket.MinHeaderLength, mulawData.Length);
//Send Bytes to Multicast
m_MulticastSender.SendBytes(bytes);

组播播放器

此示例将 ULAW RTP 组播流播放到声卡。您可以为此目的使用 Multicast Streamer。请注意,Multicast Player 的数据包大小必须大于或等于 multicast streamer 的数据包大小(标准值为 16000 字节)。当输入的抖动计数最小值为“2”时,将使用抖动缓冲区。抖动计数表示将作为最大值排队的 RTP 数据包数量。值为“0”或“1”表示未使用抖动缓冲区。(标准值为 20。)

//Create a Multicast Receiver and a SoundPlayer 
NF.MulticastReceiver m_Receiver = new NF.MulticastReceiver(PacketSize);
WinSound.Player m_Player = new WinSound.Player(); 
//Define a callback function for receiving datas from multicast
m_Receiver.DataReceived += new NF.MulticastReceiver.DelegateDataReceived(OnDataReceived);
//Connect the Multicast and open the Player
m_Receiver.Connect(MulticasAddress, MulticastPort); 
m_Player.Open(SoundDeviceName, SamplesPerSecond, BitsPerSample, Channels, BufferCount); 
//In the callback function get the rtp header of the multicast packet
WinSound.RTPPacket rtp = new WinSound.RTPPacket(bytes);
//Translate MuLaw to linear
Byte[] linearBytes = WinSound.Utils.MuLawToLinear(rtp.Data, Config.BitsPerSample, Config.Channels);
//Play the linear datas to soundcard
m_Player.PlayData(linearBytes, false);
//Close the Player and the Receiver
m_Receiver.Disconnect();
m_Player.Close(); 

历史

  • 2012年5月31日 - 添加
  • 2012年6月30日 - 在组播播放器中使用 RTP 头中的 CSRC 计数信息
  • 2012年7月8日 - Player Tester 的 WAV 文件读/写操作
  • 2012年8月11日 - 解决了在阻塞模式下调用 Player.PlayData 时出现的错误 = true
  • 2012年8月21日 - 解决了错误。垃圾回收导致应用程序崩溃
  • 2012年9月7日 - 解决了错误。读取不同格式的 WAV 文件
  • 2012年9月9日 - 播放 WAV 文件时性能更好
  • 2012年10月19日 - 添加了抖动缓冲区。添加了直接流式传输 WAV 文件
  • 2013年10月22日 - 将线性数据绘制为曲线
  • 2014年4月12日 - 添加了源代码。WinSound 实现以 C++ 编写为 COM 服务器(可选)
  • 2014年4月22日 - 解决了潜在的稳定性问题
© . All rights reserved.