15 posts tagged “c#”
When was the last time you decided you needed to deploy a product of yours. How often is it that you run into a situation where configuration and the manual to installation of your projects becomes a royal pain in the neck? Well, for most, maybe not all that often, but for the few in the corporate world - this may happen all too often.
using System.Security.Cryptography.X509Certificates;
namespace AddCert
{
class Program
{
static void Main(string[] args)
{
if (args.Length != 7 && args.Length != 5)
Console.WriteLine("Usage: addcert cert.pfx /r LocalMachine /s My /pass mypass");
string path = args[0];
StoreLocation sl = (StoreLocation)Enum.Parse(typeof(StoreLocation), args[2]);
StoreName sn = (StoreName)Enum.Parse(typeof(StoreName), args[4]);
X509Store store = new X509Store(sn, sl);
store.Open(OpenFlags.ReadWrite);
if (args.Length == 5)
store.Add(new X509Certificate2(path));
else if (args.Length == 7)
store.Add(new X509Certificate2(path, args[6]));
store.Close();
}
}
}
Every now and then it's always nice to have a little system of automation to ease your way into deployment, packaging and all sorts of interesting things. To do so, all you have to do is dance dance! :D

- Copying the target directory to a deployment folder: copy "$(TargetDir)" "$(SolutionDir)Deployment\"
- Copying the target path to the deployment folder: copy "$(TargetPath)" $(SolutionDir)Deployment\"
- Running a script to cleanup the leftovers:
Within a bat file you can select all the directories within a directory where the given name appears:FOR /F "tokens=*" %%G IN ('DIR /B /AD /S bin') DO RMDIR /S /Q "%%G" FOR /F "tokens=*" %%G IN ('DIR /B /AD /S obj') DO RMDIR /S /Q "%%G" - Running a script to stop a service, uninstall it and then re-install it, and finally start it.
net stop MyServiceName
installutil -u Location\To\MyService.exe
installutil Location\To\MyService.exe
net start MyServiceName
A lot has changed in the world of web services. This series is to get you on the right track to building incredible web services using a ridiculously easy model in the .NET 3.0 Framework.
namespace WinOrFail
{
[ServiceContract]
public interface IWantToWin
{
}
}
namespace WinOrFail
{
[ServiceContract]
public interface IWantToWin
{
[OperationContract]
bool IsThisPossible();
}
}
using System.ServiceModel;
namespace WinOrFail
{
[ServiceContract]
public interface IWantToWin
{
[OperationContract]
bool IsThisPossible();
}
[DataContract]
public class Money
{
[DataMember]
public double Amount { get; set; }
[DataMember]
public Currency Currency { get; set; }
}
[DataContract]
public enum Currency
{
[EnumMember]
US,
[EnumMember]
Euro,
[EnumMember]
Peso
}
}
A thousand times over, I will explain this one seemingly simple concept. Whenever you feel like you are running into a keyset does not exist issue. There are several plausible causes to this.


- Make sure that the certificate you are looking for is actually in the certificate store. You can see above that I am in the Local Machine certificate store. If you are attempting to load a certificate from the LocalMachine Personal Store then it should be located there.
- Does the certificate have a private key? Every certificate is associated with a private key, but it may not actually be available with the certificate itself. And, depending on how you imported the certificate, many times the private key may have been imported under your user account - but if you by chance copied a certificate into a different store, the private key may not go with it. It's important that you import the certificates to the right place the first time.
- Does your process have sufficient rights to access the certificate? With a web-application you will need to grant the web app access through a tool called winhttpcertcfg.exe. Otherwise, you will probably need to run the process under the same account that the certificate is running with. System is sufficient for a certificate in the Local Machine. For developing in Vista and above, simply run Visual Studio in Administrator mode and you should have access to it.
At my work we are working with a lot of network management related material. In light of this, I stumbled across DirectoryEntries and thus making a really simple way to list all the computers on the network (underneath all the available domains on the network).
- Add a reference to System.DirectoryServices
- Insert the using statement: using System.DirectoryServices;
- Insert the using statement: using System.Net;
- Put in the rest of the following code and you're good to go.
If you remember from my previous tutorial on Recording audio in C#, streaming from the microphone was quite simple. Within about 10 minutes of coding you were able to create a recording application that worked with any recording device plugged into or connected to your machine. In another article, I talked about my XNA TCP Networking API. In this article, I'm going to show you how easy it is to create your own voice chat application using the two apis. In this article I will be teaching you the following techniques:
- Streaming audio from the microphone
- Sending audio data over the network
- Playing network audio data back
- Visual C# Express
- TCP Networking API
- Bass.NET 2.4 (download the zip in the forum)
- Un4seen Bass 2.4 (download the zip in the forum)
It's been a while, but in my Recording audio in C# sample I used an encoder (specifically the LAME Encoder) to encode data to an Mp3 via the recording stream. Since we will not be needing any encoding we won't need the lame encoder, as well we will not need the bassenc library. To start create a new Windows Form Project, VoiceChat.
Since we need the bass.dll included in our solution, add it as en existing item. Be sure to set the property Copy to Output Directory to Copy if newer. Finally, add the Bass.NET 2.4 reference located in the .NET References tab by the Add Reference dialog. You will also need to add a reference to the Network API that you downloaded as a requirement. You can browse for the library to where you unzipped it to.
- Init - Should initialize recording and audio devices
- Record - Should create the playback and recording streams
- Play - Should play the playback and recording streams
- Stop - Should stop all playback and recording
- Free - Should free all resources and streams
- PlayBuffer - Should play a byte stream buffer
- SendBufferHandler - Delegate for sending a buffer.
This method will first initialize the default recording device followed by initializing the default audio playback device and allocating that device stream by the 8Bit flag or byte flag. The 8Bit flag correlates to our byte output stream that we will later deal with on the recording callback function. Next, the Record method.
Within this method there are three field level variables being used: myRecordProc, recordStream, and playbackStream. The first, myRecordProc, is the delegate that refers to the callback on each sample recorded from Bass. The second, recordStream, refers to the actual handle created when starting the recording channel and it is designated as an int. You can see that in Bass.BASS_RecordStart the first parameter refers to the sampling rate (44100), the second as mono output, the third as the starting flags, the fourth as the period (in milliseconds) between each sample callback, the fifth referring to the callback, and the user.
Lastly, playbackStream, uses a new feature in Bass 2.4 called push streaming. Push streaming gives us the ability to create a stream and effectively push a byte stream buffer in order to create playback. Nice, huh!? We simply pass in the frequency (sample rate) of 44100, set the channels to mono, pass in the default bass flag, and the user.
At each sample callback, we will reallocate the local myBuffer byte array when either the local buffer is null or the sample buffer length is greater than the local. It would be unwise to allocate at every interval as new can be quite costly. Next, we use the Marshal.Copy method to copy the IntPtr buffer memory into our local myBuffer at index 0 and the length being the parameter length. Lastly, we simply trigger our callback function passing in the two variables.
As I mentioned before Bass provides a new push streaming feature to actually provide the stream with a buffer in order to playback. This is done with the Bass.BASS_StreamPutData method.
The last thing we need to do for our RecordUtil is provide the Play, Stop, Free and destructor methods. We will use the corresponding Bass calls to perform each action.
And there we have it! A fully functional recording utility that we can use for the rest of this project. We need a user interface to support what we want from our application, let's design that now. At the very least, our application will be able to connect to an existing voice chat session or create a local one.
Each button refers to a call associated with the Session class located in the Networking api. Let's move into that now.
Networking
This TCP Networking API was built with bare minimum requirements for a stable TCP Network implementation. It is enhanced with a few features like multithreaded callbacks with the ability to throttle expected transfer rates. The newest addition is the Session class. This class allows you to create servers and clients fairly easily. Create a new class called NetworkUtil. We will add our network layer to the application within it. We will accomplish the following:
- Constructor - Should call Session.Init to initialize the network api.
- Join - Should call Session.Join passing in the destination ip and port
- Leave - Should call Session.Leave passing in the session id
- Create - Should call Session.Create passing in the local service port to start on
- Close - Should call Session.Close passing in the session id to close the server
- Send - Should call Session.SendData passing in the byte stream buffer and session id (as a client)
- Send - Should call Session.SendData passing in the byte stream buffer, client, and session id (as the server)
As you can see, it makes since to stop recording and sending data when we're not connected and vice-versa we should start sending and recording as soon as we are connected. The only issue here is that we would be coupling our RecordUtil with our NetworkUtil if we coded it this way. We need to create two events. Start and Stop.
With these two events, we can reuse them with our AsyncServer component. To finish, we need to replace the comments with the triggering of these two events. We can also fill in our Leave method at this time.
Next on the list is the Session Create method. Creating your own session works very much like joining one. The only difference is that you will be creating the session on your local ip address and you pass in the port. We can use the same StateChange event in the AsyncServer class located in the Session.OpenSessions dictionary. However, we will only need to worry about the ServerState.Shutdown to Stop. Starting will depend on when a client joins, as well stopping when our only client disconnects. Wire to the events and fill in the code below.
Almost done, we need to fill in the Send method. Since the data will be virtually the same, we will follow the delegate heading for SendBufferCallback in the RecordUtil class. The next step will be to write the length into a new packet along with the buffer being sent. After that, we can send the data to it's rightful owner.
The nice part about the Networking API is that sending data can be as seamless as you like. The SendData in the Session class is overloaded. You may send the data to a specific destination, or send the data to the the endpoint. The key on this method is that if you attempt to send data as a server, it assumes you mean to Broadcast the data. You can access the all the clients within a given created server with the OpenSessionClients property. Also, if you're not connected the session class will simply throw your packet away before even getting to the sockets.
In the last part, receiving the data, we will create one final event called Receive which will effectively just pass the byte stream buffer. If you noticed in the Send method we wrote the length to the buffer and then we wrote the actual byte stream based on the length provided.
To receive the actual data, we need to first find out the length of the buffer. Then once we know that, we simply grab the bytes from the data and trigger the Receive event. (Simply declare a new EventHandler called Receive at the top). You'll notice that in each of the Receive and Send I am using the number 4. I use 4 because an int is four bytes.
Finale
Excellent! I think we're ready to finish off the last details in our application. Open up the code-behind for Form1.cs. Declare two variables at the top for RecordUtil, and NetworkUtil. Next, override the onload method. In this method, create initialize the two variables and wire to each of their events. As well, we will call the RecordUtil's Init method. Be sure to wire NetworkUtil's SendBuffer event to NetworkUtil's Send method.
To ensure that our UI looks superb we will disable portions when the session has started, and enable them when the session has stopped. We can also change the text of the buttons from "Join" to "Leave" (or "Create to Close") and vice-versa. With each event in the Form code there won't be much code to insert.
When the networker determines that the session can start, the recorder can begin recording. As well, the ui can then be disabled via a method we will create in a moment. When the networker stops, the recorder should stop the recording and free the streams; as well, the ui can be enabled.
NOTE: Because this is called asynchronously we need to use the BeginInvoke method to switch to the UI thread when we are using it. To do this we simply use the EmptyDelegate that we declared at the top and use anonymous syntax to invoke.
Now we can fill in the Enable and Disable ui methods. The buttons will change text depending on which was pressed and all the other fields will be enabled or disabled.
The ui is changed based on which button is enabled (btnJoin or btnCreate). In order to make the right calls to the networking class, we need to first add the button click methods to our code. The easiest way to do this is check the text of the button to determine what action to take. ("Create" should call create, "Close" should call close, "Join" should call join, and "Leave" should call leave.
We need to call network_start when we join a session because the api will already have finished it's code structure before we can even wire to the event. As well, we need to call disable ui when btnCreate is pressed because the start event is triggered when the client joins.
We're Done!
Congratulations! You have successfully created your own Voice Chat program. To test it, put the executing assemblies on one computer and another on your computer. To determine what your IP address is, just type ipconfig in the command prompt. (Your firewall may ask you to allow the service to run.) If you both have microphones you should be hearing each other talk with an incredible quality of sound. There are plenty of upgrades we can make to this program, but we got the basics across.
Up until now, the tutorials on this blog have been about a few multimedia concepts including: streaming audio, recording from the microphone, playing internet radio, loading playlists and even networking. In this tutorial you will learn how to accomplish the following:
- Load a video from the file system and play it
- Visual C# Express
- Bass.NET 2.4 (click on forums under Bass, should be a topic on Bass.NET 2.4)
- Un4seen Bass api (version 2.4 beta) (click on forums, find bass 2.4 beta.)
- BassVideo (unofficial release of the bassvideo delphi api created by KSoft)
Once you have downloaded the above requirements you will notice that the BassVideo zip file contains a delphi library called BassVideo.dll as well as the delphi header. If you create a new C# Windows Form application and try to add a reference to this it won't work. Because the bassvideo library is not a managed library we can't simply add it directly. For this, we will need to create a wrapper according to the given delphi header. Open up the .pas file in notepad.
unit BassVideo;
interface
uses Windows;
const // video display
BassVideoDLL = 'bassvideo.dll';
BassVideo_Renderer = $100; // for BassVideo_GetConfig/SetConfigBassVideo_Default = 0;
BassVideo_VMR9 = 1;
BassVideo_VMR9_Windowless = 2;
BassVideo_EVR = 3;
BassVideo_Overlay = 4;
BassVideo_NOVIdeo = 5;
// action
BassVideo_OpenDone = 1;
BassVideo_Buffering = 2;
BassVideo_FoundVideo = 3;// BassVideo
type TVideoSync = procedure(Action, param1, param2, user : DWORD);
VIDEOPROC = procedure(Handle : DWORD; Action, param1, param2, user : DWORD); stdcall;
HSTREAM = DWORD;
TAudioInfo = record
Channels : integer;
Frequency : integer;
Bit : integer;
Float : BOOL;
SPDIF : BOOL;// reserverd, currently not supported!
DTS : BOOL;// reserverd, currently not supported!
end;TVideoType = (RGB8, RGB32, RGB24, RGB16, YUY2, YUV,
// reserved type, not supported at this time :(
YV12, YUV12, YVYU, UYVY, YUY9, Y800);//, 444P, 422P 411P );PCodecName = ^TCodecName;
TCodecName = array[0..100] of char;TVideoInfo = record
Height : integer;
Width : integer;
VideoType : TVideoType;
Codec : PCodecName;
end;QWORD = int64;
function BassVideo_ErrorGetCode : integer; stdcall; external BassVideoDLL;
function BassVideo_StreamCreate(FileName : Pointer; Flags, BassFlags : DWORD; CallBackProc : VIDEOPROC) : DWORD; stdcall; external BassVideoDLL;
procedure BassVideo_SetConfig(Option : DWORD; Value : DWORD); stdcall; external BassVideoDLL;
function BassVideo_GetConfig(Option : DWORD) : DWORD; stdcall; external BassVideoDLL;function BassVideo_Play(handle: DWORD; restart : BOOL) : BOOL; stdcall; external BassVideoDLL;
function BassVideo_Stop(handle: DWORD) : BOOL; stdcall; external BassVideoDLL;
function BassVideo_Pause(handle: DWORD) : BOOL; stdcall; external BassVideoDLL;
function BassVideo_SetPosition(handle: DWORD; pos: QWORD): BOOL; stdcall; external BassVideoDLL;
function BASSVideo_GetPosition(handle: DWORD): QWORD; stdcall; external BassVideoDLL;
function BASSVideo_GetLength(handle: DWORD): QWORD; stdcall; external BassVideoDLL;
procedure BassVideo_StreamFree(handle: DWORD); stdcall; external BassVideoDLL;
function BassVideo_Init : BOOL; stdcall; external BassVideoDLL;
procedure BassVideo_Free; stdcall; external BassVideoDLL;// for video windows function
procedure BASSVideo_SetVideoWindow(handle : HSTREAM; VideoWindowHandle : THandle; VideoRect : TRect); stdcall; external BassVideoDLL;
procedure BASSVideo_Repaint(handle : HSTREAM; WinHandle : HWND; DC : HDC); stdcall; external BassVideoDLL;
procedure BassVideo_WindowResize(handle : HSTREAM; Rect : TRect); stdcall; external BassVideoDLL;
procedure BassVideo_WindowMove(handle : HSTREAM; Msg : DWORD; wParam, lParam : LongInt); stdcall; external BassVideoDLL;
function BassVideo_SetFullScreen(handle : HSTREAM; FullScreen : BOOL) : BOOL; stdcall; external BassVideoDLL;
implementationend.
Before I move into explaining what the above code is doing, I would first like to give a brief overview on the history of delphi and it's architect Anders Hejlsberg.
Delphi, officially Borland Delphi and chief architected by Anders Hejlsberg, is known as one of the first RAD tools when it was released in 1995 for 16-bit Windows 3.1. Delphi 2, released a year later, supported 32-bit Windows environments, and a C++ variant, C++ Builder, followed a few years after. Hejlsberg would later move to Microsoft in 1996, where he worked on Visual J++, and was a key participant in the creation of the Microsoft .NET Framework, becoming the chief designer of C#.
In 2001 a Linux version known as Kylix became available. However, due to low quality and subsequent lack of interest, Kylix was abandoned after version 3.
Support for Linux and Windows cross platform development (through Kylix and the CLX component library) was added in 2002 with the release of Delphi 6.
Delphi 8, released December 2003, was a .NET-only release that allowed developers to compile Delphi Object Pascal code into .NET CIL. It was also significant in that it changed its IDE for the first time, from the multiple-floating-window-on-desktop style IDE to a look and feel similar to Microsoft's Visual Studio.NET. But still, the new IDEs have the "Classic Undocked" windows layout available, for a classical look and feel.
Okay, now that that's underway, we can see that we can compare a few object-oriented concepts from C# to that of Delphi. The first on the list is constants.
All of the variables BassVideoDLL, BassVideo_Renderer, BassVideo_Default are considered constants in delphi. As such, we can create those constants in our own class, BassVideo.
In the delphi code, we see that it specifies a procedure with the parameters: Handle, Action, param1, param2 and user; followed by assigning it to the variable name VIDEOPROC. The equivalent is the delegate shown above. You'll notice that the HSTREAM is being assigned to DWORD (also known as our int in C#). This is simply allowing types to translate or cast implicitly. After that, there is a record being specified.
TAudioInfo = record
Channels : integer;
Frequency : integer;
Bit : integer;
Float : BOOL;
SPDIF : BOOL;// reserverd, currently not supported!
DTS : BOOL;// reserverd, currently not supported!
end;
A record, in the plainest since is the equivalent of a struct in C#. We can replicate this in the following code snippet.
Next, the delphi header specifies the equivalent of an enumeration with the following snippet:
TVideoType = (RGB8, RGB32, RGB24, RGB16, YUY2, YUV,
YV12, YUV12, YVYU, UYVY, YUY9, Y800);//, 444P, 422P 411P );
We can replicate this like we would create any enum as in the following code snippet.
Now that that's done we can move onto functions and procedures. A procedure in delphi is a method that does not return anything in C#. (Or it returns void). The only catch here is that in order to use each of these functions as a reference from C#, we need to use the DllImport attribute. We can import any unmanaged dll into C# by using this attribute in the following manner.
[DllImport("nameofdll.dll")]
Awesome! Now all we have to do is use the api in our media player. I'm going to use the previous MyRadio application and add a few things to get it working. Open up your previous project and add the bass (version 2.4) and bassvideo library to your solution. Make sure to set the Copy to Output Directory to Output if newer. Then, replace the Bass.NET library reference to the new 2.4 version. Your solution explorer should look something like the following.public class BassVideo
{
// Video display
public const string BassVideoDLL = "bassvideo.dll";
public const int BassVideo_Renderer = 100;public const int BassVideo_Default = 0;
public const int BassVideo_VMR9 = 1;
public const int BassVideo_VMR9_Windowless = 2;
public const int BassVideo_EVR = 3;
public const int BassVideo_Overlay = 4;
public const int BassVideo_NOVIdeo = 5;// Action
public const int BassVideo_OpenDone = 1;
public const int BassVideo_Buffering = 2;
public const int BassVideo_FoundVideo = 3;public delegate void VIDEOPROC(int Handle, int Action, int param1, int param2, int user);
public struct TAudioInfo
{
public int Channels;
public int Frequency;
public int Bit;
public bool Float;
public bool SPDIF;
public bool DTS;
}public enum TVideoType
{
RGB8, RGB32, RGB24, RGB16, YUY2, YUV, YV12, YUV12, YVYU, UYVY, YUY9, Y800
}public struct TVideoInfo
{
public int Height;
public int Width;
public TVideoType VideoType;
public IntPtr Codec;
}[DllImport("bassvideo.dll")]
public static extern int BassVideo_GetErrorCode();[DllImport("bassvideo.dll")]
public static extern int BassVideo_StreamCreate(string FileName, int Flags, int BassFlags,
VIDEOPROC CallBackProc);[DllImport("bassvideo.dll")]
public static extern void BassVideo_SetConfig(int Option, int Value);[DllImport("bassvideo.dll")]
public static extern int BassVideo_GetConfig(int Option);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_Play(int handle, bool restart);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_Stop(int handle);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_Pause(int handle);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_SetPosition(int handle, long pos);[DllImport("bassvideo.dll")]
public static extern long BassVideo_GetPosition(int handle);[DllImport("bassvideo.dll")]
public static extern long BassVideo_GetLength(int handle);[DllImport("bassvideo.dll")]
public static extern void BassVideo_StreamFree(int handle);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_Init();[DllImport("bassvideo.dll")]
public static extern void BassVideo_Free();[DllImport("bassvideo.dll")]
public static extern void BASSVideo_SetVideoWindow(int handle, IntPtr VideoWindowHandle, Rectangle rect);[DllImport("bassvideo.dll")]
public static extern void BASSVideo_Repaint(int handle, IntPtr WinHandle, IntPtr DC);[DllImport("bassvideo.dll")]
public static extern void BassVideo_WindowResize(int handle, Rectangle Rect);[DllImport("bassvideo.dll")]
public static extern void BassVideo_WindowMove(int handle, int Msg, long wParam, long lParam);[DllImport("bassvideo.dll")]
public static extern bool BassVideo_SetFullScreen(int handle, bool FullScreen);
}
After that, open up the Form that you will use to display the video. Add a picture box to the form. Here is mine:
Now that we have something the display the video on, we need to modify our load code to be able to take in a video. Since there are plenty of types, we need to know whether a file is a video. Luckily, we can determine the supported types from the Bass api using the variable SupportedStreamName. This property shows the possible music extensions supported by the Bass api. I have created a function in the Player class below.
Now, in order to support this case when a file gets loaded we need to modify our original Load function. I have changed the original Load function to simply load in the playlist or add to it and the code that actually loads the file is in the LoadIndex function (called by the form when an index is selected on the playlist).
Since streaming video from the internet is currently unavailable to the api, I use the IsLocal to determine whether or not we need to use the LoadVideo, LoadSong, or LoadURL functions. Both the bassvideo and bass api use our stream declaration in the Player class. Hence, we need to declare a state when a video is loaded. Once we have that boolean set we can create the LoadVideo function below.
Notice the callback variable being used in the BassVideo.BassVideo_StreamCreate method call. The callback is a delegate reference whenever the video is processed on certain actions. If you remember, one of the parameters in the delegate is Action which is an integer. We can compare this to processing states also declared as constants in the api like BassVideo.BassVideo_FoundVideo. When we have that we can call the BassVideo.BASSVideo_SetVideoWindow to our picture box that we declare before. Since the picture box is something in the form, we need to create this function in our form code.
Since this code is in our form code, we need to let the Player class know about it. I have modified the constructor to include the BassVideo.VIDEOPROC as a parameter. I have also added a field variable called callback.
NOTE: Bass.NET version 2.4 updated the Bass_Init to use IntPtr when referring to the user. I have specified IntPtr.Zero.
In the constructor here, I am also initializing the BassVideo api by calling the BassVideo.BassVideo_Init method. The last thing we need to do is modify our Player to use either Bass or BassVideo depending on the videoLoaded boolean field. This is modified in the FreeStream, PlaySong and StopSong methods.
The very last thing we need to do is update our destructor to free the bassvideo api by calling the BassVideo.BassVideo_Free method.
And that's it! Hit F5, load up a video and see your new beauty shine. In this tutorial you learned about a new bass video api developed by a member of the Bass api community. You learned about delphi and even converted a delphi library header into C# not to mention you completed another aspect of multimedia within your media player.
[Source Code]
Internet radio, a phenomena that appeared in the early 90's that soon made it's way to it's grand stature today. Sure politics are trying to destroy it, but it's not going away any time soon. There is a great deal of history in Internet Radio from war protests to social networking. The latest advent in internet radio, Pandora, from the Music Genome Project, combines the analysis of each song to provide users the ability to listen to similar music; thus creating their own stations based on style, rhythms, lyrics and more. Nevertheless, I suggest you review the history on Wikipedia. I believe the next step in Internet Radio is playing from your mobile phone which doesn't seem that far off. [note project]
At any rate, our little music player doesn't seem to support internet radio at all. Well, I simply won't have that! Especially because supporting internet streams is so very simple with the Bass.NET api. In this tutorial you will learn to accomplish the following:
- Play streaming audio from an internet radio station.
- Visual C# Express (sorry, Visual C# Express 2005 is no longer available)
- Bass.NET 2.3 (scroll down until you find the .NET 2.0 link)
- Un4seen Bass api (top download link for Windows)
I am going to create a new Windows Form project called MyRadio. To start, you will need to add the reference to the Bass.NET api. Then copy the bass.dll to your output directory by including it in your project followed by setting the Copy to Output Directory property to Copy if newer. Your solution explorer should look like the following:
Once you have that setup look for the Player code from the previous tutorial. If you can't find it, you will find the code layout here.
We are going to modify this class to support internet radio streams. Open up the Player class and add the following code snippet.
public void LoadURL(string url)
{
stream = Bass.BASS_StreamCreateURL(url, 0,
BASSStream.BASS_SAMPLE_FLOAT, null, 0);
}
Bass provides this function for loading a stream from a url. In most cases, it won't matter if the stream is a file or a broadcasting stream; bass handles this for you.
You'll notice that this function looks a lot like our original. Except, instead of StreamCreateFile we are using StreamCreateURL. With the fourth parameter, specified as null, is a callback to the function to receive the stream as it's being downloaded. Also, the BASSStream contains a few flags that may allow us to obtain tags about the music including the title, author, genre and more. There are many flags that have a very specific purpose, but for now we are just going to use the default like in our previous case.
The only thing left for us to do is to provide access in our user interface. I have simply copied our previous UI and added a textbox to allow the url to be entered. When you're done, your user interface may look like the following.
There you go. Hit F5 type in an address to an internet radio station or an hosted song online and you should be good to go. In this tutorial you learned how to use Bass to stream internet radio. In my next tutorial I will be teaching you how to create your own playlist allowing for even more support for internet radio stations.
You will need the following requirements to complete this tutorial:
Recording from the Microphone
Recording: the process of capturing data or translating information to a format stored on a storage medium (or record). Lucky for us, analog-to-digital converters (ADC) take care of the translation for recording devices like a microphone. The only catch for us is providing a sampling rate against the analog wave. We used sampling when we streamed audio in our previous tutorial and we'll use it again here. The only difference about sampling from a recording device is that we are working with a different device. The code looks just about the same.
Setup a quick project that you can use for this tutorial. I created a new Windows Form project. You can create a whatever you like to complete the tutorial. Next, add a utility class called "RecordUtil.cs". Then add all the necessary references, make sure you set the Copy to Output Directory to "Copy if newer".
Add references
Taking a look at the left. Your solution explorer should contain the additions I mentioned before. That is:
- bass.dll
- bassenc.dll
- lame.exe
Having those in your project and output directory allows the final executing code to reference them without any issues running on different machines.
Next, we need to initialize the actual recording device. In the constructor of the RecordUtil place the following code snippet.
By initializing with the device 0, we will use the first recording device found by the Bass api.
Next, we're going to create the recording stream. You will need to set the sampling rate, the number of channels, BASSRecord flags, a callback function, and a user. The following code accomplishes this.
The first parameter is the sampling rate. This is the rate at which the analog wave is sampled (41000 times per second). The second parameter is the number of channels: 1 is mono, 2 is stereo. The third parameter is either the resolution (BASS_DEFAULT 16-bits, BASS_SAMPLE_8BITS 8-bit, BASS_SAMPLE_FLOAT - all of these choices will start the recording) BASS_RECORD_PAUSE starts the recording paused. Lastly, the fourth parameter is the callback function on each sample and the last parameter is the user associated with the recording.
Automatic versus manual recording
BASS has two methods of recording audio. When you do not specify null for the RECORDPROC callback function you must obtain the actual channel data using the BASS_ChannelGetData method. This is known as manual recording. Manual recording allows lower latency for full-duplex recording (i.e. playing the data as it's being recorded).
In an overloaded method for the BASS_RecordStart function, the third parameter is actually the period. The period defines the interval (in milliseconds) between calls to the callback function. The default is 100ms. (Minimum is 10ms, Maximum is 500ms).
If you do specify a callback to the RECORDPROC parameter, you are implementing automatic recording. All the data is obtained for you.
Then create the callback function and within the method you can simply return true. For the sake of this tutorial I will go into detail at a later post to explain the neat things you can access with the data.
LAME Encoder
In order to start recording with the parameter we chose, we need to use Channel_Play. But before we do that we are going to use the LAME encoder to stream the recording to an output file. Add the following code snippet:
I have added a field in the class called encoder. The EncoderLame class is in the Un4seen.Bass.Misc namespace. The first line simply constructs the encoder with the streaming channel (in our case the recording stream). Next, we set the OutputFile to the given path denoted output. The last lines of code are LAME specific variable configurations.
Aside from the bitrate and sample rate, Mode defines the encoding channel mode as in mono, stereo, join stereo.. etc. There are plenty of variables that you can configure, I have just configured the basics in this example. We want to sample at the same rate we created our recording channel at, use the default mode, encode according to quality, and have our bitrate at 192kbps (kilobits/sec).
Next, I have added four methods to handle different states in our recording program. They start out as the following:
The code here is pretty straight forward. When we call encoder.Start(null, 0) we are passing it a callback function and the associated user. The callback function allows us to work the encoding byte stream at each interval ourselves. This is very useful for us when we want to send the stream elsewhere like over a network.
The only thing left for us to do in this class is add the code for the Bass.Channel functions like we did in the previous tutorial on streaming audio. When you have that part done the final code should look like the following.
And there you go, the only missing piece is creating an interface the utilize this nice little recording utility. I've thrown a simple interface together to demonstrate this. When you're done with yours simply call the appropriate functions in the order: constructor, Initialize, Encode (with the output file), and setup a play, pause, and stop function.
Once you have your interface ready, you can make the calls to the functions you just created. When you're done your code should look something like the following code snippet.
Source code