tags:

views:

666

answers:

3

I have a program which produces audio signals which supposed to be played simultaneously. For this, I play an interval of 100 ms of audio stream in each 100 ms period. But I have undesired signals in beginning and ending of each 100 ms audio stream (because of DC) so that the output sound is not smooth even the value of signals is the same. My code is attached bellow. Please help me what I should do to have a correct real-time audio.

using System;
using System.Windows.Forms;
using Microsoft.DirectX.DirectSound;
using System.IO;

namespace TestSound
{
    class CSound : Form
    {
        const int HEADER_SIZE = 44;
        const bool FLAG_STEREO = true;
        const short BITS_PER_SAMPLE = 16;
        const int SAMPLE_RATE = 44100;

        int numberOfSamples;
        MemoryStream stream;
        BinaryWriter writer;
        Device ApplicationDevice = null;
        SecondaryBuffer buffer = null;
        BufferDescription description;

        public CSound()
        {
            try
            {
                ApplicationDevice = new Device();
            }
            catch
            {
                MessageBox.Show("Unable to create sound device.");
                ApplicationDevice = null;
                return;
            }
            ApplicationDevice.SetCooperativeLevel(this, CooperativeLevel.Priority);
            description = new BufferDescription();
            description.ControlEffects = false;
            stream = new MemoryStream();
            writer = new BinaryWriter(stream);
        }

        private void AddHeader()
        {
            stream.Position = 0;

            writer.Write(0x46464952); // "RIFF" in ASCII
            writer.Write((int)(HEADER_SIZE + (numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1) / 8)) - 8);
            writer.Write(0x45564157); // "WAVE" in ASCII
            writer.Write(0x20746d66); // "fmt " in ASCII
            writer.Write(16);
            writer.Write((short)1);
            writer.Write((short)(FLAG_STEREO ? 2 : 1));
            writer.Write(SAMPLE_RATE);
            writer.Write(SAMPLE_RATE * (FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE / 8);
            writer.Write((short)((FLAG_STEREO ? 2 : 1) * BITS_PER_SAMPLE / 8));
            writer.Write(BITS_PER_SAMPLE);
            writer.Write(0x61746164); // "data" in ASCII
            writer.Write((int)(numberOfSamples * BITS_PER_SAMPLE * (FLAG_STEREO ? 2 : 1) / 8));
        }

        public void Play(short[] samples)
        {
            if (ApplicationDevice == null)
                return;

            stream.Position = HEADER_SIZE;
            numberOfSamples = samples.Length;
            for (int i = 0; i < numberOfSamples; i++)
            {
                writer.Write(samples[i]);
                if (FLAG_STEREO)
                    writer.Write(samples[i]);
            }
            AddHeader();
            stream.Position = 0;

            try
            {
                if (buffer != null)
                {
                    buffer.Dispose();
                    buffer = null;
                }
                buffer = new SecondaryBuffer(stream, description, ApplicationDevice);
                buffer.Play(0, BufferPlayFlags.Default);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        static short[] samples = new short[4410]; // 100 ms
        static CSound sound;

        static void Main()
        {
            Form form = new Form();
            form.Show();

            sound = new CSound();
            Random random = new Random();
            for (int i = 0; i < samples.Length; i++)
                samples[i] = 1000; // constant value

            while (true)
            {
                sound.Play(samples);
                System.Threading.Thread.Sleep(100); // 100 ms
            }
        }
     }
}
A: 

There are a number of things wrong with this code. I'm guessing that when you run this code, you're hearing clicking or popping sounds every 100 ms. This is because of the Thread.Sleep(100) call inside the while(true) loop. Basically, your app is waiting 100 ms (give or take a small time amount) then calling Play(), which does a little processing and then queues up the array for playback. As a result, there is a little time gap between the playing of each 100 ms array, which is producing the clicks.

However, if you just commented out the Thread.Sleep(100) line, your app would go into an infinite loop where it keeps queueing up 100 ms array after 100 ms array until you ran out of memory. But at least the playback would not have the artifacts every 100 ms.

If you changed the line to Thread.Sleep(80), it would work a little better, in the sense that it would take you longer to run out of memory, but this would still happen because you would still be dumping buffers into the audio playback system faster than the system can play them.

Also, even if you get rid of the clicking every 100 ms, you still won't hear anything at all coming out of your speakers, because your code is setting every sample value to 1000. You will only ever hear anything if you vary the sample values over time. Incidentally, the only reason you're hearing clicks at all is because this sample value is set to 1000, and during those little time intervals between chunks the playback value goes back to 0. If you set each sample value to 0, you would never hear anything at all.

I could help you further with this, but I'd need to have a better idea of what you're trying to do, exactly. Are you trying to play a continuous tone at a certain frequency?

MusiGenesis
A: 

If your looking for a way to play audio out through a defined stream, have you considered NAudio http://naudio.codeplex.com/?

You can define a stream, from either a file or some other location (i.e. memory) and then populate the stream with the data you want played back. As long as you are able to continue providing audio data to the stream before the read pointer arrives at the end of the buffer, you wont hear these artefacts in the generated audio.

BTW - I assume you know that the Managed Direct X libraries for .Net are no longer being developed and is effectively a dead end for this sort of Audio development?

Sebastian Gray
A: 

Thank you for your suggestions. All I need to do is simple (play real-time audio signals) and I think it's not necessary to switch to another kit. the provided code is not my desire. It's just a simplification for test. In my real program I need to play data which are read from input port. So I need to play real-time audio data as follow:

public class Test
{
    CSound sound = new CSound();

    void Run()
    {
        int[] data;

        port.Read(data);
        sound.Play(data);
    }
}

the problem is not concerned with the processing time because if you cut down the Thread.Sleep(100) then the problem will not be wiped out. and notice that buffer.Play() function is asynchronous. My guess is that the solution is to add new stream to audio buffer stream (SecondaryBuffer) in a safe way without cancelling previous data. I'm not sure this is feasible because the format of a stream which is passed to SecondaryBuffer should be in specific format, and therefore the data should be determined in advance. Hence, artifacts will be found when I call Play with new data whether or not the previous data were played completely. when buffer is destroyed and new buffer with new stream is created the value of audio returns to zero and when the Play is called the artifact is generated. I try to solve this problem with several SecondaryBuffer but it doesn't work. does anyone have any suggestion?

hossein