Sign in to follow this  
paulecoyote

[.net] DirectSound C# Generate White Noise / Single tone?

Recommended Posts

Hi, I'm trying to generate some white noise or just a single tone from in sound buffer and play it. Really I want to make my own Stream class that can be fed to a SecondaryBuffer and constantly play. Just as an exercise really and a prelude to something else I'm working on. What I'm getting is "value does not fall within expected range" ArgumentException. If someone could post a link to a simple example of something generating data for a secondary buffer without loading anything from any wave files, in C#, that would be great. Cheers, Paul

Share this post


Link to post
Share on other sites
I was trying to do a direct sound app in a console application (duh) which couldn't work because you have to set the cooperation with a window handle.

anyway here's a code snippet that does work.


using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using Microsoft.DirectX.DirectSound;

namespace Noisey
{
/// <summary>
/// Very simple test form
/// </summary>
public class MainForm : System.Windows.Forms.Form
{
private System.Windows.Forms.Button buttonWhiteNoise;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

Device applicationDevice;

public MainForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// Add any constructor code after InitializeComponent call
//
applicationDevice = new Device();
applicationDevice.SetCooperativeLevel(this, CooperativeLevel.Normal);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.buttonWhiteNoise = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// buttonWhiteNoise
//
this.buttonWhiteNoise.Location = new System.Drawing.Point(168, 48);
this.buttonWhiteNoise.Name = "buttonWhiteNoise";
this.buttonWhiteNoise.Size = new System.Drawing.Size(104, 23);
this.buttonWhiteNoise.TabIndex = 0;
this.buttonWhiteNoise.Text = "WhiteNoise";
this.buttonWhiteNoise.Click += new System.EventHandler(this.buttonWhiteNoise_Click);
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(292, 260);
this.Controls.Add(this.buttonWhiteNoise);
this.Name = "MainForm";
this.Text = "Noisey";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}


private void buttonWhiteNoise_Click(object sender, System.EventArgs e)
{

WaveFormat format = new WaveFormat();
format.BitsPerSample = 8;
format.Channels = 1;
format.BlockAlign = 1;

format.FormatTag = WaveFormatTag.Pcm;
format.SamplesPerSecond = 8000; //sampling frequency of your data;
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;

// buffer description
BufferDescription desc = new BufferDescription(format);
desc.DeferLocation = true;
desc.BufferBytes = 3 * format.AverageBytesPerSecond;

// create the buffer
//Device ApplicationDevice = new Device();

SecondaryBuffer secondaryBuffer = new SecondaryBuffer(desc,applicationDevice);


//generate ramdom data (white noise)
byte[] rawsamples = new byte[22050];
Random rnd1 = new System.Random();

for (int i = 0; i < 22050; i++)
{
//-----------------------------------------------
//Completely random
//add a new audio sample to array
rawsamples[i] = (byte) rnd1.Next(255);
//-----------------------------------------------


//-----------------------------------------------
//-- Sine wave? (comment out for white noise)
int convert = (int) ( Math.Sin(i) * Math.PI );
for(int index=0; index<2; index++)
{
i+=index;
rawsamples[i] = (byte)(convert >> (index*8));
}
//-----------------------------------------------
}

//load audio samples to secondary buffer
secondaryBuffer.Write(0,rawsamples, LockFlag.EntireBuffer);

//play audio buffer
secondaryBuffer.Play(0,BufferPlayFlags.Default);

}
}
}





... although this doesn't use a custom stream, it does make a fixed width white noise / wave if you want it too.

This thread was very useful:
http://www.gotdotnet.com/Community/MessageBoard/Thread.aspx?Id=162824&Page=2&Size=25

http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_m/directx/sound/playingsounds/directsound_buffers.asp

http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/htm/dsbufferdesc.asp

http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_m/directx/sound/playingsounds/using_wav_data.asp



... This code from GotDotNet could also be useful:
Quote:

Hi Guys, I've done it, finally, heres some code that works!!!

This will capture your sound from the default Capture device, then copy it to a MemoryStream (mystream), which is linked to (mybuff), mybuff is copied to the "Data" part of a RIFF WAVE file (Thanks Cobra582!!) then is played.

I noticed that the volume is very low, and looking at the contents of mybuff shows values between 0x70 and 0x80, I'll have to work on that. This file is 1 channel and 8 bits (I have no intention of using this for home cinema quality sound) - Note that there are probably other ways of doing this (ie direct transfer of streams), but I really needed to expose the data so I can manipulate it and perform some DSP functions on it. This might help (ricky casson) as you could transfer the data using some TCP/IP socket (some sort of VOIP I suspect?)

Anyway, thanks guys, a useful forum indeed.



long DataLength = 40000; //0xD9B4;
long SampleRate = 0x5622; //samples per second.
int Channels = 1;
int BitsPerSample = 8;
int BytesPerSample = (BitsPerSample / 8) * Channels;
long BytesPerSec = SampleRate * BytesPerSample;
long length = DataLength + 36; //4+24+8


Capture c = new Capture();

CaptureBufferDescription cpd = new CaptureBufferDescription();
cpd.BufferBytes = 40000;

WaveFormat fmt = new WaveFormat();

fmt.AverageBytesPerSecond = (int)BytesPerSec;
fmt.BitsPerSample = (short)BitsPerSample;
fmt.Channels = (short)Channels;
fmt.SamplesPerSecond = (int)SampleRate;
fmt.BlockAlign = 1;
fmt.FormatTag = WaveFormatTag.Pcm;

cpd.Format = fmt;
CaptureBuffer cb = new CaptureBuffer(cpd,c);
cb.Start(false);
int pos=0;
int pos1=0;
while(pos < 39999){
cb.GetCurrentPosition(out pos,out pos1);
}
cb.Stop();

byte[] mybuff = new byte[40000];
MemoryStream mystream = new MemoryStream(mybuff,0,40000,true,true);
cb.Read(0,mystream,40000,LockFlag.None);

Random rnd = new Random((int)(new DateTime()).Date.Ticks);

byte[] initialsound = new byte[length+8];
char[] headerdata = {'R','I','F','F',
(char)(length & 0xFF),
(char)(length >> 8 & 0xFF),
(char)(length >> 16 & 0xFF),
(char)(length >> 24 & 0xFF),
'W','A','V','E','f','m','t',' ',
(char)0x10,(char)0,(char)0,(char)0,(char)0x01,(char)0x00,
(char)(Channels & 0xFF), (char)(Channels >> 8 & 0xFF),
(char)(SampleRate & 0xFF),
(char)(SampleRate >> 8 & 0xFF),
(char)(SampleRate >> 16 & 0xFF),
(char)(SampleRate >> 24 & 0xFF),
(char)(BytesPerSec & 0xFF),
(char)(BytesPerSec >> 8 & 0xFF),
(char)(BytesPerSec >> 16 & 0xFF),
(char)(BytesPerSec >> 24 & 0xFF),
(char)(BytesPerSample & 0xFF), (char)(BytesPerSample >> 8 & 0xFF),
(char)(BitsPerSample & 0xFF), (char)(BitsPerSample >> 8 & 0xFF),
'd','a','t','a',
(char)(DataLength & 0xFF),
(char)(DataLength >> 8 & 0xFF),
(char)(DataLength >> 16 & 0xFF),
(char)(DataLength >> 24 & 0xFF)
};

//copy the header data to the sound byte array.
for(int i=0;i<44;i++){
initialsound[i] = (byte)headerdata[i];
}
for(int i =44;i<DataLength+44;i+=2){

initialsound[i] = mybuff[i-44];
}

MemoryStream mystream1 = new MemoryStream(initialsound,0,(int)(DataLength+44),true,true);

snd = new SecondaryBuffer(mystream1,ApplicationDevice);
snd.Play(0,BufferPlayFlags.Looping);



Share this post


Link to post
Share on other sites
Here is the source for a thread based directsound playback class. It uses a delegate to generate the sound for the playback thread. It's not that configurable, but it does have pause/resume functionality.


namespace GarboDev
{
using System;
using System.Threading;
using System.Windows.Forms;
using Microsoft.DirectX.DirectSound;

public delegate void PullAudio(short[] buffer, int length);

public class SoundPlayer : IDisposable
{
private Device soundDevice;
private SecondaryBuffer soundBuffer;
private int samplesPerUpdate;
private AutoResetEvent[] fillEvent = new AutoResetEvent[2];
private Thread thread;
private PullAudio pullAudio;
private short channels;
private bool halted;
private bool running;

public SoundPlayer(Control owner, PullAudio pullAudio, short channels)
{
this.channels = channels;
this.pullAudio = pullAudio;

this.soundDevice = new Device();
this.soundDevice.SetCooperativeLevel(owner, CooperativeLevel.Priority);

// Set up our wave format to 44,100Hz, with 16 bit resolution
WaveFormat wf = new WaveFormat();
wf.FormatTag = WaveFormatTag.Pcm;
wf.SamplesPerSecond = 44100;
wf.BitsPerSample = 16;
wf.Channels = channels;
wf.BlockAlign = (short)(wf.Channels * wf.BitsPerSample / 8);
wf.AverageBytesPerSecond = wf.SamplesPerSecond * wf.BlockAlign;

this.samplesPerUpdate = 512;

// Create a buffer with 2 seconds of sample data
BufferDescription bufferDesc = new BufferDescription(wf);
bufferDesc.BufferBytes = this.samplesPerUpdate * wf.BlockAlign * 2;
bufferDesc.ControlPositionNotify = true;
bufferDesc.GlobalFocus = true;

this.soundBuffer = new SecondaryBuffer(bufferDesc, this.soundDevice);

Notify notify = new Notify(this.soundBuffer);

fillEvent[0] = new AutoResetEvent(false);
fillEvent[1] = new AutoResetEvent(false);

// Set up two notification events, one at halfway, and one at the end of the buffer
BufferPositionNotify[] posNotify = new BufferPositionNotify[2];
posNotify[0] = new BufferPositionNotify();
posNotify[0].Offset = bufferDesc.BufferBytes / 2 - 1;
posNotify[0].EventNotifyHandle = fillEvent[0].Handle;
posNotify[1] = new BufferPositionNotify();
posNotify[1].Offset = bufferDesc.BufferBytes - 1;
posNotify[1].EventNotifyHandle = fillEvent[1].Handle;

notify.SetNotificationPositions(posNotify);

this.thread = new Thread(new ThreadStart(SoundPlayback));
this.thread.Priority = ThreadPriority.Highest;

this.Pause();
this.running = true;

this.thread.Start();
}

public void Pause()
{
if (this.halted) return;

this.halted = true;

Monitor.Enter(this.thread);
}

public void Resume()
{
if (!this.halted) return;

this.halted = false;

Monitor.Pulse(this.thread);
Monitor.Exit(this.thread);
}

private void SoundPlayback()
{
lock (this.thread)
{
if (!this.running) return;

// Set up the initial sound buffer to be the full length
int bufferLength = this.samplesPerUpdate * 2 * this.channels;
short[] soundData = new short[bufferLength];

// Prime it with the first x seconds of data
this.pullAudio(soundData, soundData.Length);
this.soundBuffer.Write(0, soundData, LockFlag.None);

// Start it playing
this.soundBuffer.Play(0, BufferPlayFlags.Looping);

int lastWritten = 0;
while (this.running)
{
if (this.halted)
{
Monitor.Pulse(this.thread);
Monitor.Wait(this.thread);
}

// Wait on one of the notification events
WaitHandle.WaitAny(this.fillEvent, 3, true);

// Get the current play position (divide by two because we are using 16 bit samples)
int tmp = this.soundBuffer.PlayPosition / 2;

// Generate new sounds from lastWritten to tmp in the sound buffer
if (tmp == lastWritten)
{
continue;
}
else
{
soundData = new short[(tmp - lastWritten + bufferLength) % bufferLength];
}

this.pullAudio(soundData, soundData.Length);

// Write in the generated data
soundBuffer.Write(lastWritten * 2, soundData, LockFlag.None);

// Save the position we were at
lastWritten = tmp;
}
}
}

public void Dispose()
{
this.running = false;
this.Resume();

if (this.soundBuffer != null)
{
this.soundBuffer.Dispose();
}
if (this.soundDevice != null)
{
this.soundDevice.Dispose();
}
}
}
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Premandrake
Here is the source for a thread based directsound playback class. It uses a delegate to generate the sound for the playback thread. It's not that configurable, but it does have pause/resume functionality.

*** Source Snippet Removed ***


Hey thanks for that, could be useful! Rating +++++

Share this post


Link to post
Share on other sites
Hi,

1. I used your white noise code in directsound c# but there is a long perceptible audio "break" of silence before looping returns to the beginning to play again. Is it possible to get around this somehow?

2. Also why is desc.BufferBytes = 3 * format.AverageBytesPerSecond; and not
desc.BufferBytes = format.AverageBytesPerSecond;

3. If you change byte[] rawsamples = new byte[22050]; to say byte[] rawsamples = new byte[32050]; you get a runtime crash. WHats going on? If I change to byte[] rawsamples = new byte[23050]; it doesn't crash.

sid

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Microsoft.DirectX.DirectSound;

namespace Noisey
{
/// <summary>
/// Very simple test form
/// </summary>
public class MainForm : System.Windows.Forms.Form
{
private System.Windows.Forms.Button buttonWhiteNoise;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

Device applicationDevice;

public MainForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

//
// Add any constructor code after InitializeComponent call
//
applicationDevice = new Device();
applicationDevice.SetCooperativeLevel(this, CooperativeLevel.Normal);
}

/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.buttonWhiteNoise = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// buttonWhiteNoise
//
this.buttonWhiteNoise.Location = new System.Drawing.Point(168, 48);
this.buttonWhiteNoise.Name = "buttonWhiteNoise";
this.buttonWhiteNoise.Size = new System.Drawing.Size(104, 23);
this.buttonWhiteNoise.TabIndex = 0;
this.buttonWhiteNoise.Text = "WhiteNoise";
this.buttonWhiteNoise.Click += new System.EventHandler(this.buttonWhiteNoise_Click);
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(292, 260);
this.Controls.Add(this.buttonWhiteNoise);
this.Name = "MainForm";
this.Text = "Noisey";
this.ResumeLayout(false);

}
#endregion

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}


private void buttonWhiteNoise_Click(object sender, System.EventArgs e)
{

WaveFormat format = new WaveFormat();
format.BitsPerSample = 8;
format.Channels = 1;
format.BlockAlign = 1;

format.FormatTag = WaveFormatTag.Pcm;
format.SamplesPerSecond = 8000; //sampling frequency of your data;
format.AverageBytesPerSecond = format.SamplesPerSecond * format.BlockAlign;

// buffer description
BufferDescription desc = new BufferDescription(format);
desc.DeferLocation = true;
desc.BufferBytes = 3 * format.AverageBytesPerSecond;

// create the buffer
//Device ApplicationDevice = new Device();

SecondaryBuffer secondaryBuffer = new SecondaryBuffer(desc,applicationDevice);


//generate ramdom data (white noise)
byte[] rawsamples = new byte[22050];
Random rnd1 = new System.Random();

for (int i = 0; i < rawsamples.Length; i++)
{
//-----------------------------------------------
//Completely random
//add a new audio sample to array
rawsamples[i] = (byte) rnd1.Next(255);
//-----------------------------------------------


//-----------------------------------------------
//-- Sine wave? (comment out for white noise)

int convert = (int) ( Math.Sin(i) * Math.PI );
for(int index=0; index<2; index++)
{
i+=index;
rawsamples[i] = (byte)(convert >> (index*8));
}

//-----------------------------------------------
}

//load audio samples to secondary buffer
secondaryBuffer.Write(0,rawsamples, LockFlag.EntireBuffer);

//play audio buffer
secondaryBuffer.Play(0,BufferPlayFlags.Looping);

}
}
}

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Hello,
I have a problem with the simplest element - creating sound device
my app crushes on below line:
Device device = new Device();
Debuger says:
is not a valid Win32 application. (Exception from HRESULT: 0x800700C1)
I'm running Win XP Pro x64 and DX9c.
I appreciate any help
Dave

Share this post


Link to post
Share on other sites
if you want to take a look at my direct sound code, get in touch.
It does before-play-cursor (BPC) streaming. The playback is very reliable and very low cpu overhead, although I haven't tested other aspects like pause/resume/play-from as much.
The way I set it up, everything gets passesd in as a mono stream in wav format. It only reads as much as it has to.
It may be overkill though as well, as there are codec interfaces, etc and it's tied into my engine somewhat.

but the offer is there :-)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
I need a C# (VS 2005) class that creates new wave buffer or appends to an existing wave buffer, sounds of specific frequency and specific duration with silence if the frequency is 0.
The idea is to create buffer, add sounds of different frequency and duration by repeatedly callin a method. Once the sequence is complete the wave buffer is can be played. For this part of the game, the sound is mono.

Can someone please help me with a code snippet for this?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this