1

I am developing a simple real time audio library. Where the programmer can make calls to methods that can play audio in real-time. In addition, the programmer can call methods that can play a sine wave at specific frequency and duration in real-time. The audio format that is being played in realtime is WAV. The problem is when I call playSineWave it throws an inner exception saying the PlayerSound.Play cannot play the sound since it has a corrupt header. So at first I believed that I by accident wrote the header wrong. But I saved the actual bytes of the wav file including the header of course and it played in iTunes without error and it sounds like a sine wave at the correct frequency. So what am I doing wrong since the exception that is being thrown is misleading.

Here is the code that is causing the problem:

        public void playSineWave(int frequency, int totalSampleLength)
        {
            if (frequency <= 0)
            {
                throw new InvalidDataException("frequency cannot be zero or negative.");
            }
            List<byte> data = new List<byte>();
            byte[] header = getWavHeader(totalSampleLength);
            data.AddRange(header);
            for(int x = 0; x < (totalSampleLength); x++)
            {
                data.Add((byte)(127 * Math.Sin(2 * (Math.PI / sampleRate) * frequency * (x / 2))));
            }



            using(MemoryStream stream = new MemoryStream())
            {
                using (SoundPlayer player = new SoundPlayer(stream))
                {


                    stream.Write(data.ToArray(), 0, data.Count);
                    player.Play();



                }
            }



        }


        public int secondsToSampleLength(int seconds)
        {
            /*
             * x sample    2 byte       60 second
             * -------- * -------- * --------------- = total in samples
             * 1 second   1 second       1 second
             */
            return sampleRate * seconds * 2; 

        }
        private byte[] getWavHeader(int totalSamples)
        {
            MemoryStream stream = new MemoryStream();
            byte[] wavHeader;
            using (BinaryWriter writer = new BinaryWriter(stream, Encoding.BigEndianUnicode))
            {

                writer.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }); // 4
                int bitsPerSample = 16;
                writer.Write(36 + (totalSamples * numberOfChannels * (bitsPerSample / 8))); // 4
                writer.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }); // 4
                writer.Write(new byte[] { 0x66, 0x6d, 0x74, 0x20 }); // 4
                writer.Write(16); // 4
                writer.Write((short)1); // 2
                writer.Write((short)numberOfChannels); // 2
                writer.Write(sampleRate); // 4
                writer.Write(sampleRate * numberOfChannels * (bitsPerSample / 8)); // 4
                writer.Write((short)(numberOfChannels * (bitsPerSample / 8))); // 2
                writer.Write((short)(bitsPerSample)); // 2
                writer.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }); // 4
                writer.Write((totalSamples * numberOfChannels * (bitsPerSample / 8))); // 4
                wavHeader = stream.ToArray();
            }
            return wavHeader;
        }

Would I have to use methods from the Windows API that can play the WAV file? Any suggestion would also be helpful. Thanks in advanced. Please do not suggest NAudio I want to write WAV files from scratch and I am aware of that great library.

Forgot to mention that if I set the positon to zero after writing then it gives this exception below:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Daniel Lopez
  • 1,728
  • 3
  • 24
  • 40
  • A WAV file is *not* a byte[], it is a file format that has a structure dictated by the RIFF standard (Resource Interchange File Format). SoundPlayer does *not* know how to playback raw sample data (aka PCM). If you don't want to use NAudio then you'll have to create your own wav stream writer. – Hans Passant Mar 07 '12 at 02:02

2 Answers2

0

Well I solved it by using unmanaged code. This class below allows me to play a wav file in memory just convert the byte array to a string using the write conversion methods and I can play wav file and even a sine wave in real-time.

class UnManagedSoundPlayer  
{    
    [DllImport("WinMM.dll")]    
    public static extern bool  PlaySound(string data, int Mod, int flag);     // these are the SoundFlags we are using here, check mmsystem.h for more    
    public const int SND_ASYNC    = 0x0001;     // play asynchronously    
    public const int SND_FILENAME = 0x00020000; // use file name    
    public const int SND_PURGE    = 0x0040;     // purge non-static events     
    public const int SND_NODEFAULT = 0x00002;
    public const int SND_SYNC = 0x00000;
    public const int SND_MEMORY = 0x00004;
    public static bool Play(string data, int SoundFlags = (SND_ASYNC | SND_MEMORY | SND_NODEFAULT | SND_SYNC))    
    {      
        return PlaySound(data, 0, SoundFlags);    
    }     
    public static void StopPlay()    
    {      
        PlaySound(null, 0, SND_PURGE);    
    }  
}
Sjoerd222888
  • 3,228
  • 3
  • 31
  • 64
Daniel Lopez
  • 1,728
  • 3
  • 24
  • 40
-1

Stream.Write sets the Position of the stream to the end of the written data. Try resetting the stream's Position to 0 before calling SoundPlayer.Play().

allonym
  • 1,408
  • 10
  • 18
  • Forgot to mention that I did that but it gives a different exception. I will post it in my question. – Daniel Lopez Mar 06 '12 at 23:16
  • Try initializing the stream by passing the data buffer to the MemoryStream constructor, instead of calling Write – allonym Mar 07 '12 at 01:34
  • Using your code (with sampleRate=44100 and numberOfChannels=2), and writing the resulting wav data out to a file, I was not able to play the file with Windows Media Player. It would play in iTunes (as you stated), as well as in VLC and IrfanView, so I'm guessing that WMP is less tolerant than these other players of whatever fault lies in the wav data. Presumably, SoundPlayer is using the same rendering engine as WMP, so it can't play your stream. I'm not familiar enough with WAV to figure out where you've gone wrong (sorry!) – allonym Mar 07 '12 at 02:05