tags:

views:

910

answers:

7

I need to create a sound containing tones of many different frequencies. Is there any way to do this in C#?

The only tone generating methods I've seen so far involve console.beep, which works, but only for pure tones (single frequencies).

A: 

Yes it is possible.

Here is a link to a tutorial on this. but of course this also uses Console.Beep

Binoj Antony
From what I've seen there this also can only generate single tones at a time.
Joey
A: 

Take a look at this forum link

D. Veloper
+4  A: 

You can always try DirectSound...

MinkoSoft
Do you have a link that refers specifically to generating complex tones with DirectSound?
Tom Wright
In a nutshell you will write the results of a lot of sin() calls into a wave buffer, then have it play back that buffer.
Kylotan
A: 

The MSDN documentation doesn't make it clear if Console.Beep is asynchronous or not. If it is, you can probably fire off as many calls as you need in quick succession and nobody will be the wiser. You'd want to use the version that takes a frequency and a duration, of course.

Drew Hall
It's synchronous.
Joey
I'm new to C# - can synchronous calls be layered using threads?
Tom Wright
You can invoke Console.Beep using new thread, but you still cannot be able to play many sounds at once.
twk
@Johannes, @twk: Bummer. Nix this idea then...
Drew Hall
+3  A: 

I have been looking at NAudio with the view to create a program that emulates feedback whilst playing a backing track. There is a blog post about generating sine waves at specific frequencies, I suspect that this could be adapted to do what you are looking for.

Richard Slater
This is the most promising so far, but I've been unable to get more than one frequency running together.
Tom Wright
Have you tried using multiple threads, the documentation mentions supporting the windows mixing device.
Richard Slater
A: 

Essentially, you have to implement your own software synthesizer or find a 3rd party library. See: http://en.wikipedia.org/wiki/Demo%5F%28computer%5Fprogramming%29#Music

CannibalSmith
+15  A: 

The Audiere library makes this extremely easy to do. Here's a nearly complete C# program to generate the DTMF tone for the "1" button:

AudioDevice device = new AudioDevice();
OutputStream tone1a = device.CreateTone(697);  // part A of DTMF for "1" button
OutputStream tone1b = device.CreateTone(1209); // part B
tone1a.Volume = 0.25f;
tone1b.Volume = 0.25f;
tone1a.Play();
tone1b.Play();
Thread.Sleep(2000);
// when tone1a stops, you can easily tell that the tone was indeed DTMF
tone1a.Stop();

To use Audiere in C#, the easiest way to get up and running is to use Harald Fielker's C# binding (which he claims works on Mono and VS; I can confirm it works in the both full version of VS2005 and using the separate Express 2008 versions of C# and VC++). You'll need to download the Win32 Audiere DLL, lib, and header (which are all in the same zip) and you'll need to build the C# binding from source using both VC++ and C#.

One of the nice benefits of using Audiere is that the calls are non-blocking. You don't have to wait for tone1a to stop playing before you start tone1b, which is clearly a necessity for playing complex tones. I am not aware of any hard upper limits on how many simultaneous output streams you can use, so it's probably whatever your hardware/OS supports. By the way, Audiere can also play certain audio files (MP3, WAV, AIFF, MOD, S3M, XM, IT by itself; Ogg Vorbis, Flac, Speex with external libraries), not just pure generated tones.

One possible downside is that there is a slightly audible "click" as you start or stop an individual tone; it's not noticeable if you add one tone to an already playing tone. The easiest workaround I've found for that is to slowly ramp the tone's volume up or down when you're turning the tone on or off, respectively. You might have to play around with the ramp speed to get it to sound "just right".

Note that Audiere is LGPL-licensed, and the binding has no license attached to it. You'll have to consult your legal team or try to get a hold of Harald if you want to use his binding in a commercial product; or you could just make your own binding and avoid the hassle.


@Tom: Since there is no specific license attached to Harald's library, I'm not sure what implications would come of hosting it; however, I believe I can at least give you fine detail on exactly how my libaudieresharpglue project is set up.

Using Visual C++ Express 2008, open up bindings/csharp/libaudieresharpglue/vc8.0/libaudieresharpglue.sln. VC++ will automatically convert the solution to a VS9 solution.

In another folder, you should have the Audiere package from Sourceforge. Under your VC++ project properties, go to Configuration Properties > C/C++ > General, and make sure you have path/to/audiere-1.9.4-win32/include in your "Additional Include Directories." Then, in that same window, go to Linker > General and make sure you have /path/to/audiere-1.9.4-win32/lib in your "Additional Library Directories." Then, you should be able to build the project (preferably in Release mode) and this output libaudieresharpglue.dll in your vc8.0/Release folder.

Next, open up Visual C# Express 2008. Open up bindings\csharp\test\vc8.0\AudiereCSharpTest.sln and let it convert the solution. The project should build fine, but then you will get an error when you run it. That's fine; in your csharp/test/vc8.0/bin/Release folder, you need to add both libaudieresharpglue.dll from the VC++ solution and audiere.dll from the package from Sourceforge.

Now, you should be able to build and run AudiereCSharpTest. Note that by default, #define stream_test is not commented out at the top of AudiereTest.cs, and that will reference a file that is not on your hard drive. You can simply comment out that #define and uncomment noise_test or square_test.

That should cover it; if I missed any details, hopefully they are small enough to get by on your own :)

Mark Rushakoff
That's a nice looking library, might take a closer look if NAudio dosn't work out for me.
Richard Slater
I'm having big problems compiling the "libaudieresharpglue.dll" part of the wrapper to compile. Any idea where I can just grab a binary?
Tom Wright
I've accepted this because it's obviously what I need to do. Despite your idiot proof explanation however, I still can't get "libaudieresharpglue.dll" to compile (stops at 100 errors). When I link, it tells me it cannot open input file '.\Release\audiere.obj' - is there something elementary I'm missing?
Tom Wright
Generally, you need to fix compiler errors before you worry about linker errors. I can't tell if I left out something important above, so you might want to open up a new question for compiling the library -- the C# and C++ tagged questions usually do get answered very promptly, after all.
Mark Rushakoff
Just revisited this and it's working like a charm. Thanks Mark!
Tom Wright
@Tom: Glad it finally worked out for you :)
Mark Rushakoff