Jump to content
  • Advertisement
Sign in to follow this  
Oduig

Processing sound input with DirectSound.Notify

This topic is 2540 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

In a little spare-time project of mine I am attempting to make an application that can process sound from a real guitar through the Line in (microphone) jack. The 'game' will have to show an accurate and timely response to what was played on the guitar. The sort of latency I am looking for is in the order of 10 milliseconds. If the delay is any higher, the lag becomes noticeable and this is incredibly confusing for the player.

After setting up the structure and some basic functionality, I noticed that the library I used (SoundCapture + SoundAnalysis from http://www.codeproje...uitarTuner.aspx) only produces a maximum of two notes a second, which is 500ms for each short[] audio sequence. I traced the delay by doing my own measurements on the code, and from what I can see the bottleneck appears to be the DirectSound.Notify object. If I understand correctly, DirectSound should set an autoresetevent when the audio buffer has a certain amount of data. The SoundCaptureBase class (pasted below) will wait for this event and process the data.

The problem is, it looks like DirectSound indeed takes 500ms to pass on audio data. I cannot seem to find any settings that purposedly delay the incoming audio event. Where is the mistake, or how should things be done differently to get my audio input stream running at a faster pace?


Thank you for any suggestions and merry Christmas to all,
Guido

using System;
using System.Threading;
using Microsoft.DirectX.DirectSound;
using Microsoft.Win32.SafeHandles;

namespace SoundCapture
{
/// <summary>
/// Base class to capture audio samples.
/// </summary>
public abstract class SoundCaptureBase : IDisposable
{
const int BufferSeconds = 3;
const int NotifyPointsInSecond = 2;

// change in next two will require also code change
const int BitsPerSample = 16;
const int ChannelCount = 1;

int sampleRate = 44100;
bool isCapturing = false;
bool disposed = false;

public bool IsCapturing
{
get { return isCapturing; }
}

public int SampleRate
{
get { return sampleRate; }
set
{
if (sampleRate <= 0) throw new ArgumentOutOfRangeException();

EnsureIdle();

sampleRate = value;
}
}

Capture capture;
CaptureBuffer buffer;
Notify notify;
int bufferLength;
AutoResetEvent positionEvent;
SafeWaitHandle positionEventHandle;
ManualResetEvent terminated;
Thread thread;
SoundCaptureDevice device;

public SoundCaptureBase()
: this(SoundCaptureDevice.GetDefaultDevice())
{

}

public SoundCaptureBase(SoundCaptureDevice device)
{
this.device = device;

positionEvent = new AutoResetEvent(false);
positionEventHandle = positionEvent.SafeWaitHandle;
terminated = new ManualResetEvent(true);
}

private void EnsureIdle()
{
if (IsCapturing)
throw new SoundCaptureException("Capture is in process");
}

/// <summary>
/// Starts capture process.
/// </summary>
public void Start()
{
EnsureIdle();

isCapturing = true;

WaveFormat format = new WaveFormat();
format.Channels = ChannelCount;
format.BitsPerSample = BitsPerSample;
format.SamplesPerSecond = SampleRate;
format.FormatTag = WaveFormatTag.Pcm;
format.BlockAlign = (short)((format.Channels * format.BitsPerSample + 7) / 8);
format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;

bufferLength = format.AverageBytesPerSecond * BufferSeconds;
CaptureBufferDescription desciption = new CaptureBufferDescription();
desciption.Format = format;
desciption.BufferBytes = bufferLength;

capture = new Capture(device.Id);
buffer = new CaptureBuffer(desciption, capture);

int waitHandleCount = BufferSeconds * NotifyPointsInSecond;
BufferPositionNotify[] positions = new BufferPositionNotify[waitHandleCount];
for (int i = 0; i < waitHandleCount; i++)
{
BufferPositionNotify position = new BufferPositionNotify();
position.Offset = (i + 1) * bufferLength / positions.Length - 1;
position.EventNotifyHandle = positionEventHandle.DangerousGetHandle();
positions = position;
}

notify = new Notify(buffer);
notify.SetNotificationPositions(positions);

terminated.Reset();
thread = new Thread(new ThreadStart(ThreadLoop));
thread.Name = "Sound capture1";
thread.Start();
}

private void ThreadLoop()
{
buffer.Start(true);
try
{
WaitHandle[] handles = new WaitHandle[] { terminated, positionEvent };
double elapsed;

// Testing code to find the notification rate of DirectSound
DateTime dt;
int count = 0;
dt = DateTime.Now;
while (WaitHandle.WaitAny(handles) > 0) {
// This is where one would process the audio data
count++;
if (count % 10 == 0) {
elapsed = (DateTime.Now - dt).TotalMilliseconds;
// Breakpoint
elapsed = 0;
}
}
}
finally
{
buffer.Stop();
}
}

/// <summary>
/// Processes the captured data.
/// </summary>
/// <param name="data">Captured data</param>
protected abstract void ProcessData(short[] data);

/// <summary>
/// Stops capture process.
/// </summary>
public void Stop()
{
if (isCapturing)
{
isCapturing = false;

terminated.Set();
thread.Join();

notify.Dispose();
buffer.Dispose();
capture.Dispose();
}
}

void IDisposable.Dispose()
{
Dispose(true);
}

~SoundCaptureBase()
{
Dispose(false);
}

private void Dispose(bool disposing)
{
if (disposed) return;

disposed = true;
GC.SuppressFinalize(this);
if (IsCapturing) Stop();
positionEventHandle.Dispose();
positionEvent.Close();
terminated.Close();
}
}
}

Share this post


Link to post
Share on other sites
Advertisement
Now that Christmas and the forum issues are over, I was going to clean up the above code to make it easier to read and bump the topic. Unfortunately, I do not seem to be able to edit my post. Let me just add that the only relevant code is in the method Start(). The code in ThreadLoop simply waits until it gets a 'bufferready' event and posts the elapsed time, which as of now is still 500 milliseconds.

Any help would be greatly appreciated :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!