Search code examples
c#windowssdltao-framework

Tao.Sdl Audio crash on Windows


I have a short little piece of code to play tones via Tao.Sdl. It works fine on Linux and Mac (using Mono), but it crashes on Windows7, 64-bit with Mono or .NET. Here is the code:

using System;
using System.Runtime.InteropServices;
using Tao.Sdl;

namespace ConsoleApplication1
{
    class Program
    {
        delegate void AudioSpecCallbackDelegate(IntPtr userData, IntPtr stream, int length);
        static Sdl.SDL_AudioSpec desired;
        static IntPtr specPtr;
        static IntPtr obtained;

        static void Main(string[] args)
        {
            Sdl.SDL_Init(Sdl.SDL_INIT_AUDIO);
            desired = new Sdl.SDL_AudioSpec();
            desired.freq = 22050; 
            desired.format = (short)Sdl.AUDIO_S16LSB;
            desired.channels = 1;
            desired.samples = (short)1000; //(short)2205; 
            desired.callback = Marshal.GetFunctionPointerForDelegate((AudioSpecCallbackDelegate)DoCallback);
            desired.userdata = null;
            specPtr = Marshal.AllocHGlobal(Marshal.SizeOf(desired));
            Marshal.StructureToPtr(desired, specPtr, false);
            obtained = IntPtr.Zero;
            if (Sdl.SDL_OpenAudio((IntPtr)specPtr, (IntPtr)obtained) < 0)
            {
                Console.WriteLine("Error opening sdl_audio (1)");
                Console.WriteLine(Sdl.SDL_GetError());
            }
            Sdl.SDL_PauseAudio(0);
            Sdl.SDL_Delay(1000);
            Sdl.SDL_PauseAudio(1);
        }

        public static void DoCallback(IntPtr userData, IntPtr stream, int len)
        {
            Console.WriteLine("{0}, ", len);
            byte[] buffer = new byte[len];
            for (int i = 0; i < len; i++)
                buffer[i] = 0;
            Marshal.Copy(buffer, 0, stream, len);
        }
    }
}

It crashes on Windows the second time it tries to call the callback. Can you see anything that I am doing obviously wrong? Perhaps I don't have the right values for freq, or perhaps the format matters on Windows?

Alternatively, I can't figure out how to debug the low-level code... it just crashes in Visual Studio or in MonoDevelop. Or, if you have suggestions on re-doing this code using a different system. Goal: be able to handle the bytes to be played through the sound system in C# on Mac, Windows, and Linux.


Solution

  • I ended up rewriting the code in SdlDotNet, even though that has another bug which requires a window to exist. I simply create a small window, and close it after I open the stream. The following works on Linux and Windows7. Need to test on Mac. I never did discover what the above bug was... perhaps it is related to the need for a window. The following also has the actual code for producing a tone. I've seen some pretty buggy examples in SDL to do this.

    public class AudioManager : IDisposable {
        const int playbackFreq = 44100;
        const int samples = 2048;
        const double pi2 = 360 * Math.PI / 180.0;
        private bool disposed = false;
        private bool initialized = false;
        SdlDotNet.Audio.AudioStream stream;
        byte[] buffer8;
    
        double time = 0;
        double volume;
        double frequency1 = -1;
        double frequency2 = -1;
        SdlDotNet.Audio.AudioCallback audioCallback = new SdlDotNet.Audio.AudioCallback(Callback)
    
        public AudioManager()
        {
            stream = new SdlDotNet.Audio.AudioStream(playbackFreq, 
                                 SdlDotNet.Audio.AudioFormat.Unsigned8, 
                                 SdlDotNet.Audio.SoundChannel.Mono, 
                                 samples, 
                                 audioCallback, 
                                 null);
            buffer8 = new byte[samples];
            volume = 1.0;
    
            // BUG: OpenAudio (or lower) apparently requires a visible screen for some reason:
            SdlDotNet.Graphics.Video.SetVideoMode(1, 1);
            SdlDotNet.Audio.Mixer.OpenAudio(stream);
            // BUG: close (or hide) it
            SdlDotNet.Graphics.Video.Close();
        }
    
        public void beep(double duration, double freq) {
            frequency1 = freq;
            frequency2 = -1;
            Tao.Sdl.Sdl.SDL_PauseAudio(0);
            Tao.Sdl.Sdl.SDL_Delay((int)(duration * 1000));
            Tao.Sdl.Sdl.SDL_PauseAudio(1);
        }
    
        void Callback(IntPtr userData, IntPtr stream, int len)
        {
            double slice = 1.0 / playbackFreq * pi2; 
            for (int buf_pos = 0; buf_pos < len; buf_pos++ )
            {
                buffer8[buf_pos] = (byte)(127 + Math.Cos(time) * volume * 127);
                time += frequency1 * slice;
                if (time > pi2)
                    time -= pi2;
            }
            Marshal.Copy(buffer8, 0, stream, len);
        }