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

nBASS: A sound libary for .NET

By , 10 Nov 2002
 

Introduction

Many times I have seen people asking in the forums for sound support for .NET. Unfortunately, we had to make do with difficult unmanaged dll's or use the media player interface. Then one day I came along a "little" sound library called BASS. This is a sound library that provides you with various static functions for playing, creating, streaming and recording sound. It has support for WAV/MP3/MP2/MP1/OGG sound formats as well a variety of popular MOD formats. With the addition of their WMA addon, the library supports WMA playback as well as WMA creation and network streaming. Having a VB.NET API on the BASS website, I used that as my starting point. NOTE: You must download the BASS and BASSWMA package from the BASS website and copy the 2 dll's to the application folder.

Design

One of the big problems with consuming unmanaged code is the fact that you do not deal directly with objects, but rather with handles to undefined objects. But more on that later. Like I said my starting point was the VB.NET "API". All it is really is a mapping to all the functions provided by the BASS dll. Still that was too unfriendly for me. I then started rewriting all the functions over to C#, and adding more meaningful functions to be used from within .NET. In the code you will notice all unmanaged calls start with an underscore (_) and they usually will have a "friendly" public counterpart.

After remapping all the functions, I started working on the class hierarchy. Several options down the line, I finally came up with a solution that would work. This was essential as several of the static functions can be preformed on a variety of objects (handles). Basically, I created a base class (ChannelBase) and let my defined objects inherit from there or from an object already inheriting from  ChannelBase. In some cases like Stream and Music, I created an intermediate abstract class (AdvancedChannel) from which they inherited. All in all, the structure works very well and saves a lot on duplicate code. Unfortunately, when consuming unmanaged dll's there always seems to be a fair amount of duplicate code (obviously having the source to BASS would have simplified things, but I am not that lucky).

Having the structure in place, I started designing my own classes for managed use. Basically, I went by wrapping a class around a handle and "hiding" the handle from the user and let him/her rather work with an object directly. On object creation, a handle would be passed to the constructor and the resulting object would be returned. I opted not to have public constructors for my objects as most need to have BASS initialized first. So once a BASS class was created, further objects can be created from there, with the exception of the CD and Record class.  They are allowed to be instantiated with public constructors.

Having objects (with handles) made it possible for me to rewrite all unmanaged functions from:

static int UnmanFunc(IntPtr handle, int someparam);

to

void ManFunc(int someparam);

All "freeing" functions from BASS dll has also been implemented into their respective finalizers, so the implementer does not have to worry about freeing up unmanaged resources. I didn't go for the IDisposable model as it seemed to create more "work" than that is supposed to prevent. UPDATE: IDisposable has been implemented.

An exception model was also implemented, by using the static GetErrorCode() function provided. In other words, the user does not check for an error, but rather wait for an exception.

An event model was also implemented for when the "sound" stops playing. The marshalling required for this was quite tricky. You can have a look inside AdvancedChannel.

Finally, it came round to testing all the functions provided by the BASS dll. There were some really "weird" (from a C# perspective anyways) marshalling to be done to get the functions to behave like they should. I had to implement some classes to solve some problems, for example the ChannelAttributes class. This is really only a "placeholder" for some variables , making life alot easier when having to modify some settings. You can also have a look at the FX class to see how I handled the void * type from my code.

Testing

I have created various test projects to test various functions. Here are some screenshots:

CD (very basic):

Stream (with stereo visualization):

Music (with 3D positioning and EAX presets):

WMAStream (with stereo visualization, playing previously encoded WMA file):

WMAEncoder (encoding to network):

Implementation

The implementation is very easy and a sound can be played with very little work.

BASS bass = new BASS(-1 , 44100, DeviceSetupFlags.Default, 
            new IntPtr(0)); //we use foreground window
bass.Start(); //we call this to INIT BASS.dll
Stream stream = bass.LoadStream(false, "test.mp3", 0, 0, StreamFlags.AutoFree);
stream.Play(true, StreamPlayFlags.Default);

Please note: The BASS dll requires a valid window handle to function, so either pass IntPtr.Zero (foreground window) or the form's handle.

Documentation

I have tried to do as much as possible for documentation, but it still lacks huge parts. I have included an nDoc generated help file with most of the documentation coming straight from the BASS dll's docs. I have tried to keep the functions as closely related to their static unmanaged counterparts.

I have since first public release also included a changelog as the project is still under heavy development, but mostly functional.

Conclusion

Playing sounds from within C# cant be easier than this. If you have any comments, suggestions, remarks, please feel free to contact me. This was my first project of such a kind, but I believe it was successful.

Updates

nBASS 0.9.5 (9 September 2002)

- Fixed bug in WMAStream.GetTags()
- Added IDisposable to classes (big thanks to Joel Mueller)
- ChannelBase and AdvancedChannel is now abstract (again, thanx to Joel)
- Change exceptions to show message when exception is thrown without having to do it manually :)

nBASS 0.9.6 (14 September 2002)

- Fixed Stream Tags. New tag properties available.
- WMAStream now directly inherites from AdvancedChannel.
- New ID3v1Tag class available.

nBASS 0.9.6.1 (4 November 2002)

- CLSCompliant :)
- Support for BASS version 1.7+

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

leppie
Software Developer
South Africa South Africa
No Biography provided
Follow on   Twitter

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   
GeneralRe: How can i control the volume of two streamssusschi-tai25-Jul-04 7:14 
hi grv575,
 
thank you for your help. i haven't tried it this way, because i found a solution Smile | :)
 
just create an Attribute-Object as a copy of the old one. modify the volume and set the clone the new one to the Stream-Object.
 
chi-tai
GeneralRe: How can i control the volume of two streamssusschi-tai25-Jul-04 7:19 
ups some code snippet would be useful Big Grin | :-D
 
// here we create a new, but you can also create a clone of the old one to preserve the values
ChannelAttributes StreamAtt = new ChannelAttributes();
 
// value = any value or the property value...
StreamAtt.volume = value;
BassStream.Attributes = StreamAtt;
 
chi-tai
GeneralRecording \ Writing to a wav filemembereranation9-Apr-04 17:08 
Hi
 
I was just finishing a little test with the VB.NET API and I must tell you it was not fun.
 
what you did, was what the authors of the bass api should have done in the first place... Smile | :)
 
what my app needs to do, and I'm still figuring that out, is to write different .wav samples in different location in the target file (the app is a little midi drum machine, with wave samples, and I would like to have an option to render the output to a wave file)
I'm sure that its possible, all I'm lacking is to know how to create a file output stream and to control what to write to it and when (like how to write silence, just put a 0 byte?? ) as you can see I have no knowledge of wave file format... so any help will be VERY appreciated Big Grin | :-D
 
I've tried to work with the original documentation but it was not helpful at all (to me)
 
Thanks!
 
Eran Medan

QuestionWhere can I get the Old DLLs?memberLeon v Wyk17-Mar-04 23:18 
The new bass.dll v.2 is the only one available and does not seem to work with this sample. Where can I get hold of the old v.1.6? Or an update to this sample?
 
The new dll is missing some of the Entry Points like "BASS_SetGlobalVolumes" and "BASS_GetGlobalVolumes"
 
Leon v Wyk
AnswerRe: Where can I get the Old DLLs?sussLundvaldMusic6-Jul-04 9:14 
I have the same problems. Nobody else ?
 
The version of nBASS (1.8) i have found (from http://nBASS.sourceforge.net) seems to require version 1.8 og BASS and BASSWMA.
 
The only versions of BASS and BASSWMA i can find out there are version 2.0.
AnswerRe: Where can I get the Old DLLs?sussAnonymous11-Oct-04 15:02 
http://un4seen.com/files/bass18.zip
GeneralRe: Where can I get the Old DLLs?sussTim Gordon10-Mar-05 2:22 
Excellent! This has helped me out, too.
 
I was struggling for ages to get nBass 1.8 to work with Bass 2.1
GeneralRe: Where can I get the Old DLLs?memberTearsofwind8-Jan-07 16:29 
I also have the problem. When I entered www.un4seen.com and found no entry to get older bass.dll such as version 1.7. So could mail it to me when anyone find it Or tell me the nBass could run with lastest bass.dll?
Email: wdz163ren@163.com
Thanks a lot!
GeneralIllegal Device Numbermembertonictickle27-Feb-04 22:04 
Hello!
 
If I want to start the nBASS-Project or my Project with the nBASS Reference, I get an Exception in VS.NET 2003:
 
An unhandled Exception of the type 'nBASS.BASSException' is in nbass.dll...
 
Further Informations: Illegal Device Number
 
What have I to do?? I only want to play a MP3-File or maybe an Audio-CD with nBASS.
 
Thanks!!
GeneralRe: Illegal Device Numbermembertonictickle29-Feb-04 9:08 
When I change the code line to:

bass = new BASS(0, 44100, 0, this.Handle);
bass.Start();
 
stream = bass.LoadStream(false, txtBrowse.Text, 0, 0, 0);
stream.Play(true, StreamPlayFlags.Default);

 
the Error above is away, but there's a new one:
 
The requested data is not available
GeneralRe: Illegal Device NumbermemberQAF18-May-04 12:47 
I get the same errors. Did you ever solve the problem?
 
Also, I wonder why it's -1 by default, when the help for the BASS function BASS_Init() says:
 
DWORD device [...] The device to use... 0 = no sound, 1 = first real output device
 

BTW, the programs included in the BASS2.0 download work just fine, only nBASS gives this error.
GeneralRe: Illegal Device Numbermembertonictickle18-May-04 14:09 
Unfortunately, I have no clue. The Problem is still there.
GeneralRe: Illegal Device Numbermembergrv57515-Jul-04 20:20 
Don't know what the problem is so I'll just paste the code that I use.
 
private nBASS.BASS bass1;
private nBASS.AdvancedChannel stream;
bass1 = new BASS();
bass1.Start();
stream = bass1.LoadStream(filename, 0, 0, StreamFlags.AutoFree);
stream.Play(true, StreamPlayFlags.Default);

GeneralRe: Illegal Device Numbermembertonictickle16-Jul-04 3:03 
Thanks, but that doesn't work for me either.
 
I use this:
 
using nBASS;
 
private nBASS.BASS bass1;
private nBASS.AdvancedChannel stream;
 
bass1 = new BASS(-1 , 44100, DeviceSetupFlags.Default, new IntPtr(0));
bass1.Start();
stream = bass1.LoadStream(false, txtBrowse.Text, 0, 0, StreamFlags.AutoFree);
stream.Play(true, StreamPlayFlags.Default);
 
I changed bass1 = new BASS(); to
bass1 = new BASS(-1 , 44100, DeviceSetupFlags.Default, new IntPtr(0));
because he wanted arguments
 
Now he says: 'nBASS.AdvancedChannel' does not contain a definition for 'Play'
I changed that to
private nBASS.Stream stream;
 
For this he wants 5 Arguments:
stream = bass1.LoadStream(filename, 0, 0, StreamFlags.AutoFree);
so I added (true, ...)
 
Now the project starts, but with the same Error when I want to play a MP3-File: Illegal Device Number
 
Maybe something in this line is wrong: bass1 = new BASS(-1 , 44100, DeviceSetupFlags.Default, new IntPtr(0)); ???
GeneralRe: Illegal Device Numbermembertonictickle16-Jul-04 3:35 
Now, I found out, that if I change
bass1 = new BASS(-1 , 44100, DeviceSetupFlags.Default, new IntPtr(0));
to
bass1 = new BASS(0, 44100, 0, this.Handle);
the Error "Illegal Device Number" is gone and there came a new one:
"The requested data is not available".
I think, he can't find the Stream/File?!
GeneralRe: Illegal Device Numbermembertonictickle16-Jul-04 3:27 
Now, I found out, that if I change
bass1 = new BASS(-1 , 44100, DeviceSetupFlags.Default, new IntPtr(0));
to
bass1 = new BASS(0, 44100, 0, this.Handle);
the Error "Illegal Device Number" is gone and there came a new one:
"The requested data is not available".
I think, he can't find the Stream/File?!
Generalend of song eventmemberfguihen24-Jan-04 0:21 
when a song ends, what event is passed, from the stream, or the bass object, as i want my program to go straight into playing another song in a playlist as soon as the end of song event is triggered( if there is such an event)
Generalchannel data flagsmemberfguihen7-Jan-04 3:26 
i have a visualisation up and running , using your code as an example as the help file is very bare. can you give me more info on the channel data flags that i use, eg SFFT2048. i see there are a few of theese flags but the difference each one is beyond me. also , i got the trackbar to move using your code as an example, but i also dont understand the process fully. can you expand on this at all for me??
GeneralRe: channel data flagsmemberleppie7-Jan-04 6:07 
Please refere to the BASS documentation. I dont know what it does either.
 
leppie::AllocCPArticle("Zee blog");
Seen on my Campus BBS: Linux is free...coz no-one wants to pay for it.

GeneralReal time encoding with WMAencodermemberb.joe18-Dec-03 23:06 
Hi!
 
I would like to write a telephone programm and I need to encode recorded data immediately. My problem is that, as I see, a WMAencoder object can
only encode to a file on disk, given by a string-filename.
Is it - somehow - possible to encode to a Stream(.net object) instead
of a file?
 
I found only this in wmaencode.cs:
[DllImport("basswma.dll", EntryPoint = "BASS_WMA_EncodeOpenFile")]
private extern static IntPtr _OpenFile(int freq, int flags, int bitrate, string filename);
 
I was speaking about the last parameter.
 
Thank you

GeneralRe: Real time encoding with WMAencodermemberleppie19-Dec-03 6:11 
You wanna stop right now. WNAEncoding have about a 20 second delay. There is nothing u can do about it.
 
Hmmm | :|
 
leppie::AllocCPArticle("Zee blog");
Seen on my Campus BBS: Linux is free...coz no-one wants to pay for it.

GeneralRe: Real time encoding with WMAencodermemberb.joe19-Dec-03 7:40 
I don't care about 20msec delay. I would be happy, with 100msec delay!
I would like to record e.g 80 msec wav, encode it and send it.
(I want data travell in xml in a HTTP request to be able to work
via firewalls. Dataspeed (3000byte/sec) and everything is calculated,
I only need a mp3/wma (.net)encoder )
I looked after other dll-s (lame_enc, and mpglib/mpg123), but I'm not
sure I would be able to write a .net wrapper for them. They look
complicated for meFrown | :-(
 
So do you have any suggestions?
 
PS: nBass playback is realy great!!!
GeneralRe: Real time encoding with WMAencodermemberleppie19-Dec-03 7:52 
20 seconds, not milliseconds....
 
leppie::AllocCPArticle("Zee blog");
Seen on my Campus BBS: Linux is free...coz no-one wants to pay for it.

Questionhow to initialise an nBass object with ver 1.8memberfguihen29-Nov-03 1:47 
hi. you were right. i only had ver 9.6.??. now im having trouble initialising a bass object. what parameters are needed? i havnt a clue what an Icontainer is. thank you and sorry to bother you
AnswerRe: how to initialise an nBass object with ver 1.8memberleppie29-Nov-03 2:25 
Just add nBASS.dll to the toolbox, and simply drag 'n drop the nBASS component to the form. All the options can be set via the properties in the designer, even plays a funky tune. Smile | :)
 
leppie::AllocCPArticle("Zee blog");
Seen on my Campus BBS: Linux is free...coz no-one wants to pay for it.

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.130619.1 | Last Updated 11 Nov 2002
Article Copyright 2002 by leppie
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid