Click here to Skip to main content
Click here to Skip to main content

Play or Capture Audio Sound. Send and Receive as Multicast (RTP)

By , 18 Oct 2012
 
Download Exe_2.zip
Download source

Introduction      

It is not comfortable to use the Sound API in .NET, because there is no support by the class library itself. So I decided to write my own small Audio Assembly WinSound.dll by using PInvoke and the native WMM API (Windows Multi Media). In addition i wrote some Network Assemblies, to stream and play Audio Data over Network by Multicast.

Background 

The WaveOut and WaveIn functions of WMM are very tricky, because you have to know some special features of how to work with the internal buffers and handles. For example you can not stop a recording, without waiting for all buffers to finish. You are not allowed to stay too long in the Callback functions. You have to swap from managed to unmanaged code. The Repeater is the most sensitive one, because you have to balance the recording and playing buffers all the time.

Using the code   

The assemblies which are to be used as DLLs are 

For common sound operations  

  • WinSound.dll      

For network operations   

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

Example Projects are 

  • MulticastPlayer      (Plays a RTP-Multicast to Soundcard)
  • MulticastStreamer  (Sends a RTP-Multicast from Soundcard) 
  • Player Tester         (Plays or records a wav-File)
  • RepeaterTester      (Plays from Soundcard to Soundcard)  

The heart of all this is WinSound.dll. All example projects need this DLL (add as reference). 

Using the Sound Recorder   

Note that buffersize and buffercount must be optimized to your Sounddevice and used 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()
{
    
}

Using the Sound Player  

Note that buffercount must be optimized to your Sounddevice and used 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();
//Stop Playing
player.Stop();
//Close Player
player.Stop();
					
//Called, when player is stopped 
private void OnPlayerStopped()
{

}

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

}  

Linear Bytes

The linear data bytes you write or read from soundcard, are depending on BitsPerSample and Channel Count. Each "Data Packet" is to interpret as this picture shows.

The Player (Recorder) Tester

This example uses WinSound.dll to play or record a Wav-file. SamplesPerSecond, BitsPerSample and Channels are only used in record mode.

//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);  

The Repeater Tester

This example is recording Sound-Data from one sound device (WaveIn) to another (WaveOut). Every sound device has its special features, so you have to change the BufferCount and BufferSize for optimal performance. It also differs from operating System Windows-XP, Vista or 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();

The Multicast Streamer

This example Streams ULAW RTP-Multicast data from a sound device or a Wav-File. When using sound device streaming, it is possible to synchronise it with the "Time Sync" checkbox. Buffer Count is the amout of buffers, that are used by capturing from sound-device (Use 8 as standard). You can use the Multicast Player to play this Multicast. Also you can use the VLC Media Player for testing (Use Samples Per Second = 8000 and BitsPerSample = 16 with VLC).

//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 reveiving 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);
 

The Multicast Player

This example plays a ULAW RTP-Multicast stream to a sound device. You can use the Multicast Streamer for this purpose. Note, the packet size of the Multicast Player must be greater or equal than the packet size of the multicast streamer (Use 16000 Bytes as standard) . A Jitter Buffer is used, when entering a minimum value of "2" as jitter count. Jitter count means the amount of RTP-Packets that will be queued as maximum. Values of "0" or "1" means, there is no Jitter Buffer in use. (Use 20 as standard)

 

//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(); 

History  

  • 31.05.2012 - Added.
  • 30.06.2012 - Updated. Using the CSRC Count Information in RTP Header for Multicast Player.
  • 08.07.2012 - Updated. Wav-File Read/Write operations for Player Tester.  
  • 11.08.2012 - Updated. Error resolved, when calling Player.PlayData in blocking mode = true
  • 21.08.2012 - Updated. Error resolved. Garbage Collection caused Application crash.
  • 07.09.2012 - Updated. Error resolved. Reading Wave-Files in different formats.
  • 09.09.2012 - Updated. Better performance when playing wav files.
  • 19.10.2012 - Updated. Added Jitter buffer. Added streaming wav-files directly. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Banjoo
Software Developer Telecommunication
Germany Germany
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionfull duplexmemberMember 877069513-Jun-13 4:12 
I managed to combine your clases and projects into a single very simple chat application for a personal local Network in my neighborhood, but I have a question: "Can you please help with an ideea on how to set up your TCP/IP connection as a full duplex ?" Thanks
AnswerRe: full duplexmemberBanjoo13-Jun-13 23:34 
Hi,
 
Multicast (UDP) is never duplex. Either you use two multicasts for each direction (Sending + Receiving) or better you use a TCP (duplex) connection. See my other article "TCP Streamer" for example.
 
Greetings
Banjoo
GeneralRe: full duplexmemberMember 877069513-Jun-13 23:59 
Thanks... I will look at that example for sure
QuestionTCPmemberKenneta7-Jan-13 6:49 
Hi,
 
Is it possble to use direct TCP (point to point) instead of multicast?
 
Can you add support for OPUS codec, this sounds very good even on low bitrates. http://www.opus-codec.org[^]
AnswerRe: TCPmemberBanjoo7-Jan-13 11:36 
Hi,
 
You can look at my "Tip" for a TCP connection client and server streaming application, it is very similar. In the moment I have no time for implementing other codecs, maybe in the future.
 
Greetings Banjoo
SuggestionRe: TCPmemberKenneta11-Jan-13 0:31 
Thanks!
 
Regarding OPUS, I found this C# example for Opus encode/decode. I think it will work with your applications, I will give it a try... https://github.com/JohnACarruthers/Opus.NET[^]
Questionstereo wavsmemberMember 968028712-Dec-12 10:45 
I am trying to stream some stereo files (using the stream file option and setting the number of channels to 2) but it seems like the audio signal at the receiving side is monophonic. Is the 2 channels wav file option supported in this project?
 
Thanks a lot!!!
 
PS. It is an excellent demonstration of an audio streaming application
AnswerRe: stereo wavsmemberBanjoo13-Dec-12 6:23 
Hi,
 
yes indeed, the tool is a half-stereo project. In most cases the stereo datas on both channels are the same. So i decided to send only the datas of one channel by RTP to avoid network traffic. The receiver reproduces the datas, so you hear stereo in fact. But only "simulated stereo" with copied datas of channel one. So of course it could be possible to hear nothing, when channel one is noiselessly although channel two has audible datas.
 
Look at this functions in "Utils.cs"
 
a) public static Byte[] LinearToMulaw(Byte[] bytes, int bitsPerSample, int channels)
b) public static Byte[] MuLawToLinear(Byte[] bytes, int bitsPerSample, int channels)
 
If you want, you can update these functions to send in stereo. But note, you will have the double of network traffic.
 
Greetings Banjoo
QuestionQuestionmemberhighlight5530-Oct-12 0:40 
I try to send multiple multicast streams in one thread but sometimes the program give me a exception in the multicast timer "CallbackonCollectedDelegate". It´s possible to use the SoundSender class in a multiple thread system?
AnswerRe: QuestionmemberBanjoo30-Oct-12 1:02 
You can use all assemblies in a multiple thread system. Your problem seems to be a garbage collection issue. When you`re working with unmanaged code, you have to ensure to hold your native referenced objects in memory. Use GCHandle operations to "Pin" your objects, so no garbage collection removes or moves your objects. In my code you will find some GCHandles for example.
GeneralRe: Questionmemberhighlight5530-Oct-12 2:32 
Thanks a lot for your response and for your code it's really good. I already try your solution and i think that is solve my problem. I have experience in Java but no in C#, I'm a beginner. I think that your delegate method responsible for the error is the time event handler in Win32 class because the exception say this "WinSound!WinSound.Win32+TimerEventHandler::Invoke".
I fix when i call GCHandle.Alloc(s); and the "s" is a object of SoundSensor class. It isn't the most better way because it´s alloc some many variables but is solved. Thanks.
QuestionMulticastPlayer IP issuememberZypper22-Oct-12 9:29 
Hi Banjoo
 
I am about to test MulticastStreamer and MulticastPlayer over a private network, but programs does not accept IP addresses other than between 224.0.0.0 and 239.255.255.255, i would like it to also work on the private area 192.168.0.0 to 192.168.255.255
 
Hoverer i have looked at the code and changed the Multicaststreamer so that the
IP check goes from 192
 
// Überprüfe ob es sich um eine gültige IPv4 Multicast-Adresse handelt
public static bool isValid(string ip)
{
try
{
int octet1 = Int32.Parse(ip.Split(new Char[] { '.' }, 4)[0]);
if ((octet1 >= 192) && (octet1 <= 239))

 
It seems to work with the Multicaststreamer
But it does not work if i change it in the Multicastplayer, I got System.Net.Socket.SocketException
when i try to set address 192.168.1.6
Do you know what can cause this ?
 
//
AnswerRe: MulticastPlayer IP issuememberBanjoo22-Oct-12 11:36 
Hi zypper,
 
Do not mix TCP Addresses with Multicast Addresses. There are no privat Multicast Addresses, better, there are only privat ones. Multicast streaming is most used in private LANs. So you better use addresses in the granted range, this is controlled by the operating system.
 
Greetings Banjoo
GeneralRe: MulticastPlayer IP issuememberZypper23-Oct-12 7:53 
Hi Banjoo
 
Ok, I am not so familiar with IP numbers so i did not know what that range was used for.
 
But i have a VPN tunnel from another site that has a local IP network on 192.168.1.0 and the home site i am on is 192.168.0.0 and i was tried to send sound from a shortwave receiver. And Multicast streamer only works if i enter destination address 192.168.0.3 and then Multicastplayer works if i enter any IP number in the multicast area, so I think multicastplayer listens to any IP.
 
So I cannot send sound on a multicast address over the VPN network, i hear nothing.
But it works fine if i enable multicaststreamer to use my PC address 192.168.0.3
and the sound is great and in sync so give it 5 stars Smile | :)
 
//
GeneralRe: MulticastPlayer IP issuememberBanjoo23-Oct-12 12:44 
Hi Zypper,
 
well, Multicast is not transfered over subnets and routers automatically. You have to configure your router or VPN connection for several multicast addresses and ports to forward them. I have posted a new tip/trick with an extended TCP Streamer which uses TCP as transfer Protocol and not Multicast. TCP needs no forwarding and has no data lost like Multicast. Maybe this could help you.
 
Search for: TCP Audio Streamer and Player
 
Greetings Banjoo
GeneralMy vote of 5memberDr Bob24-Sep-12 9:54 
Very impressive work in a difficult area within Windows. Thanks!
GeneralWow !memberarthurscime9-Sep-12 20:53 
Wow ! It's really great that now we can Play sound, capture and record sound, repeat sound from sound device to sound device. It's really very useful & beneficial too.
Corporate speakers are the one who not only know what to say, but they know how to say it as well.

GeneralWow !memberarthurscime9-Sep-12 20:52 
Wow ! It's really great that now we can Play sound, capture and record sound, repeat sound from sound device to sound device. It's really very useful & beneficial too.
GeneralMy vote of 5memberMazen el Senih7-Sep-12 8:01 
Awesome !!
GeneralMy vote of 5memberChristian Amado22-Aug-12 5:29 
5
GeneralMy vote of 5memberTage the great11-Aug-12 17:25 
Superb codes and applications!
GeneralMy vote of 5memberDr Bob10-Jul-12 14:58 
Wundebar!
QuestionThanks a lot for such a nice submission., Is it possible to use the player/without admin rights on the system?memberSyed Ashar Akhtar9-Jul-12 4:02 
Infact i wish to use the in a teaching application where teacher broadcasts voice while students use lab PC without admins rights. I tried the system but it requires admin permission for the listening part. Is it possible to avoid those.
 
Kind regards
 
Ash
AnswerRe: Thanks a lot for such a nice submission., Is it possible to use the player/without admin rights on the system?memberBanjoo9-Jul-12 7:31 
Hello,
 
i guess you mean with player the multicast-player. I developed my software without any admin restrictions. But to play or receive a multicast is also an intern operating-system operation. So you could get in trouble with firewalls, routers or just rights permissions, there´s no way out.
 
If you can, use Win-XP, its not so strict with admin rights. Or ask your Admin back for a coffee Java | [Coffee]
SuggestionCleaned up .zipmemberRavi Bhavnani30-Jun-12 11:39 
I cleaned up the .zip file you uploaded and was able to reduce its size by more than 90%.  This is what your .zip file should contain.
 
Thanks,
 
/ravi
My new year resolution: 2048 x 1536
Home | Articles | My .NET bits | Freeware
ravib(at)ravib(dot)com

GeneralRe: Cleaned up .zipmemberBanjoo30-Jun-12 13:08 
Allright, thanks.
GeneralRe: Cleaned up .zipmemberRavi Bhavnani30-Jun-12 15:04 
Thanks for fixing that.  I've added my approval for publication.
 
/ravi
My new year resolution: 2048 x 1536
Home | Articles | My .NET bits | Freeware
ravib(at)ravib(dot)com

QuestionDownloads?memberVitaly Tomilov30-Jun-12 5:53 
Where are the downloads?
Let's agree to disagree!
Boris the animal Just Boris.

AnswerRe: Downloads?memberBanjoo30-Jun-12 7:42 
Oh ... I forgot. Should be included in last update
GeneralRe: Downloads?memberVitaly Tomilov30-Jun-12 9:50 
got them, thank you!
Let's agree to disagree!
Boris the animal Just Boris.

GeneralRe: Downloads?memberRavi Bhavnani30-Jun-12 11:31 
Please remove .svn and binary content from your .zip file to reduce unnecessary bloat.  Thanks!
 
/ravi
My new year resolution: 2048 x 1536
Home | Articles | My .NET bits | Freeware
ravib(at)ravib(dot)com

QuestionMessage RemovedmemberKenneta21-Jun-12 22:20 
Message Removed

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130617.1 | Last Updated 18 Oct 2012
Article Copyright 2012 by Banjoo
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid