w-ll
w-ll

Reputation: 3915

How can i synthesize and output simple sound waves in C#?

If you have a lisp, please dont try and say my title.

But yes, whats the easiest way to generate simple, or Bonus for more complex sawtooth and other waves.

Upvotes: 4

Views: 2785

Answers (4)

dumbledad
dumbledad

Reputation: 17527

There is a Charles Petzold article Simple Electronic Music Sequencer for Silverlight based on Gilles Khouzam's blog entry Playing back Wave files in Silverlight and Pete Brown's Creating Sound using MediaStreamSource in Silverlight 3 Beta. Mike Hodnick has a useful blog post with sample code based on the Petzold article called Digital Audio Synthesis on Windows Phone 7.

In the XAML of the main window Mike introduces a media element

<MediaElement x:Name="media"/>

and then uses the SetSource method to set the media elements source to the wave he's constructing

this.media.SetSource(new TonesSource());
this.media.Play();

Mike's ToneSource subclasses his BaseSource which in turn subclasses MediaStreamSource and overrides several of its methods: OpenMediaAsync, GetSampleAsync, SeekAsync, CloseMedia, GetDiagnosticAsync, and SwitchMediaStreamAsync. There's more about them in the MSDN documentation; Mike's code itself isn't long but involves bit shifting and writing to memory streams and is worth looking at in the source provided in Mike's blog post.

Mike's ToneSource class then provides stereo samples

protected override StereoSample GetSample()
{
    short left = 0;
    short right = 0;

    foreach (var oscillator in this.leftOscillators)
        left += oscillator.GetNextSample();

    foreach (var oscillator in this.rightOscillators)
        right += oscillator.GetNextSample();

    return new StereoSample() { Left = left, Right = right };
}

using his Oscillator class

public short GetNextSample()
{
    ushort wholePhaseAngle = (ushort)(phaseAngle >> 16);
    short amplitude = 0;
    amplitude = (short)(short.MaxValue * Math.Sin(2 * Math.PI * wholePhaseAngle / ushort.MaxValue));
    amplitude = (short)((amplitude * multiplier) >> 16);
    phaseAngle += phaseAngleIncrement;

    return amplitude;
}

The NoiseSource Mike also provides is even simpler than his ToneSource

protected override StereoSample GetSample()
{
    return new StereoSample()
    {
        Left = (short)random.Next(short.MinValue, short.MaxValue),
        Right = (short)random.Next(short.MinValue, short.MaxValue)
    };
}

Upvotes: 0

Mark Heath
Mark Heath

Reputation: 49482

Have a look at this question for sample code on how to generate a sine wave in C#. Extending that to square or sawtooth waves would be pretty easy. You can generate more complex waveforms by mixing together simple ones.

You can also use the NAudio WaveFileWriter class to write the generated data into a WAV file if you require.

Upvotes: 1

Ana Betts
Ana Betts

Reputation: 74654

Just to elaborate on what Jon already said - all you do is create an 8-bit (i.e. byte[1024]) or 16-bit buffer and fill it out (i.e. for a square wave it's [255 255 255 255 0 0 0 0 255 255 255 255 0 0 0 0]).

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500085

In my experience the .wav file format (the Wikipedia entry lists other documents giving the file format; use whichever you find best) is the simplest widely-used one. It's pretty easy to write uncompressed PCM.

For things like sine waves and sawtooths, I'd split the task into sampling and file production. So you might have an interface such as:

public interface IWave
{       
    double Sample(double time);
}

where Sample will be given a time greater than 0 (but possibly greater than 1) and should return a value between -1 and 1. (You could use a delegate for this instead.) Then write a file generator to create a wav file based on the sample duration (e.g. "100000 waves") and sample frequency (e.g. 50000 Hz).

Then it's just a case of implementing IWave appropriately - e.g. a version which returns sin(time / (2 * pi)) for a simple sine wave, or (time % 1.0) * 2 - 1 for a sawtooth. For bonus amusement, you could write composition functions to speed up or slow down the wave, amplify it, combine other waves etc. The file generator wouldn't need to know any of that, of course. It would just need to take a single IWave and sample it the appropriate number of times, scaling the [-1, 1] range to [0, 255] appropriately.

Upvotes: 4

Related Questions