0

I am trying to make work WinOut API on Win CE (ARM) but with the same code that runs on Windows 7 I am getting MMSYSERR_INVALIDPARAM in the line for buffer preparation.

Win32.MMRESULT hr = Win32.waveOutPrepareHeader(
    hWaveOut, ref WaveOutHeaders[i], Marshal.SizeOf(WaveOutHeaders[i])); 
if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) ... 

I think that is the problem of buffer alignment on byte boundary for ARM but I do not know how to do it in .NET Compact Framework for Windows CE.

I am trying to find some solution or explanation but nothing.

UPDATE

This is the P/Invoke that works on Win CE and .NET Compact Framework. On the internet is possible to find P/Invoke related to "winmm.dll" instead of "coredll.dll". Winmm.dll exists on Windows XP, 7. Windows CE works only coredll.dll. If used "winmm.dll" on WinCE is received the following message " "Can't P/Invoke winmm.dll"

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutOpen(ref IntPtr hWaveOut, int uDeviceID, ref WAVEFORMATEX lpFormat, DelegateWaveOutProc dwCallBack, int dwInstance, int dwFlags);

    [DllImport("coredll.dll")]
    public static extern MMRESULT waveInOpen(ref IntPtr hWaveIn, int deviceId, ref WAVEFORMATEX wfx, DelegateWaveInProc dwCallBack, int dwInstance, int dwFlags);

    [DllImport("coredll.dll", SetLastError = true)]
    public static extern MMRESULT waveInStart(IntPtr hWaveIn);

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint waveInGetDevCaps(int index, ref WAVEINCAPS pwic, int cbwic);

    [DllImport("coredll.dll", SetLastError = true)]
    public static extern uint waveInGetNumDevs();

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint waveOutGetDevCaps(int index, ref WAVEOUTCAPS pwoc, int cbwoc);

    [DllImport("coredll.dll", SetLastError = true)]
    public static extern uint waveOutGetNumDevs();

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutWrite(IntPtr hWaveOut, ref WAVEHDR pwh, int cbwh);

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutPrepareHeader(IntPtr hWaveOut, ref WAVEHDR lpWaveOutHdr, int uSize);


    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern uint waveOutGetDevCaps(int index, ref WAVEOUTCAPS pwoc, int cbwoc);

    [DllImport("coredll.dll", SetLastError = true)]
    public static extern uint waveOutGetNumDevs();

[DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutWrite(IntPtr hWaveOut, ref WAVEHDR pwh, int cbwh);


[DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutPrepareHeader(IntPtr hWaveOut, ref WAVEHDR lpWaveOutHdr, int uSize);

    [DllImport("coredll.dll", EntryPoint = "waveOutReset", SetLastError = true)]
    public static extern MMRESULT waveOutReset(IntPtr hWaveOut);


    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern MMRESULT waveOutUnprepareHeader(IntPtr hWaveOut, ref WAVEHDR pwh, int cbwh);

    [DllImport("coredll.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern Win32.MMRESULT waveOutClose(IntPtr hWaveOut);

[DllImport("coredll.dll")]
    public static extern Win32.MMRESULT waveOutPause(IntPtr hWaveOut);

[DllImport("coredll.dll", EntryPoint = "waveOutRestart", SetLastError = true)]
    public static extern Win32.MMRESULT waveOutRestart(IntPtr hWaveOut);

Data Structures and constants used

public const int WAVE_MAPPER = -1;

    public const int WT_EXECUTEDEFAULT = 0x00000000;
    public const int WT_EXECUTEINIOTHREAD = 0x00000001;
    public const int WT_EXECUTEINTIMERTHREAD = 0x00000020;
    public const int WT_EXECUTEINPERSISTENTTHREAD = 0x00000080;

    public const int TIME_ONESHOT = 0;
    public const int TIME_PERIODIC = 1;

    /// <summary>
    /// WAVEOUTCAPS
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
    public struct WAVEOUTCAPS
    {
        public short wMid;
        public short wPid;
        public int vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public short wChannels;
        public short wReserved;
        public int dwSupport;
    }

    /// <summary>
    /// WAVEINCAPS
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
    public struct WAVEINCAPS
    {
        public short wMid;
        public short wPid;
        public int vDriverVersion;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string szPname;
        public uint dwFormats;
        public short wChannels;
        public short wReserved;
        public int dwSupport;
    }

    /// <summary>
    /// WAVEFORMATEX
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct WAVEFORMATEX
    {
        public ushort wFormatTag;
        public ushort nChannels;
        public uint nSamplesPerSec;
        public uint nAvgBytesPerSec;
        public ushort nBlockAlign;
        public ushort wBitsPerSample;
        public ushort cbSize;
    }

    /// <summary>
    /// MMRESULT
    /// </summary>
    public enum MMRESULT : uint
    {
        MMSYSERR_NOERROR = 0,
        MMSYSERR_ERROR = 1,
        MMSYSERR_BADDEVICEID = 2,
        MMSYSERR_NOTENABLED = 3,
        MMSYSERR_ALLOCATED = 4,
        MMSYSERR_INVALHANDLE = 5,
        MMSYSERR_NODRIVER = 6,
        MMSYSERR_NOMEM = 7,
        MMSYSERR_NOTSUPPORTED = 8,
        MMSYSERR_BADERRNUM = 9,
        MMSYSERR_INVALFLAG = 10,
        MMSYSERR_INVALPARAM = 11,
        MMSYSERR_HANDLEBUSY = 12,
        MMSYSERR_INVALIDALIAS = 13,
        MMSYSERR_BADDB = 14,
        MMSYSERR_KEYNOTFOUND = 15,
        MMSYSERR_READERROR = 16,
        MMSYSERR_WRITEERROR = 17,
        MMSYSERR_DELETEERROR = 18,
        MMSYSERR_VALNOTFOUND = 19,
        MMSYSERR_NODRIVERCB = 20,
        WAVERR_BADFORMAT = 32,
        WAVERR_STILLPLAYING = 33,
        WAVERR_UNPREPARED = 34
    }

    /// <summary>
    /// MMSYSERR
    /// </summary>
    public enum MMSYSERR : uint
    {
        // Add MMSYSERR's here!

        MMSYSERR_BASE = 0x0000,
        MMSYSERR_NOERROR = 0x0000
    }

    [Flags]
    public enum WaveHdrFlags : uint
    {
        WHDR_DONE = 1,
        WHDR_PREPARED = 2,
        WHDR_BEGINLOOP = 4,
        WHDR_ENDLOOP = 8,
        WHDR_INQUEUE = 16
    }

    [Flags]
    public enum WaveProcFlags : int
    {
        CALLBACK_NULL = 0,
        CALLBACK_FUNCTION = 0x30000,
        CALLBACK_EVENT = 0x50000,
        CALLBACK_WINDOW = 0x10000,
        CALLBACK_THREAD = 0x20000,
        WAVE_FORMAT_QUERY = 1,
        WAVE_MAPPED = 4,
        WAVE_FORMAT_DIRECT = 8
    }

    [Flags]
    public enum HRESULT : long
    {
        S_OK = 0L,
        S_FALSE = 1L
    }

    [Flags]
    public enum WaveFormatFlags : int
    {
        WAVE_FORMAT_PCM = 0x0001
    }

    /// <summary>
    /// WAVEHDR
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct WAVEHDR
    {
        public IntPtr lpData; // pointer to locked data buffer
        public uint dwBufferLength; // length of data buffer
        public uint dwBytesRecorded; // used for input only
        public IntPtr dwUser; // for client's use
        public WaveHdrFlags dwFlags; // assorted flags (see defines)
        public uint dwLoops; // loop control counter
        public IntPtr lpNext; // PWaveHdr, reserved for driver
        public IntPtr reserved; // reserved for driver
    }

    /// <summary>
    /// TimeCaps
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct TimeCaps
    {
        public UInt32 wPeriodMin;
        public UInt32 wPeriodMax;
    };

    /// <summary>
    /// WOM_Messages
    /// </summary>
    public enum WOM_Messages : int
    {
        OPEN = 0x03BB,
        CLOSE = 0x03BC,
        DONE = 0x03BD
    }

    /// <summary>
    /// WIM_Messages
    /// </summary>
    public enum WIM_Messages : int
    {
        OPEN = 0x03BE,
        CLOSE = 0x03BF,
        DATA = 0x03C0
    }

    public delegate void DelegateWaveOutProc(IntPtr hWaveOut, WOM_Messages msg, IntPtr dwInstance, ref Win32.WAVEHDR wavehdr, IntPtr lParam);
    public delegate void DelegateWaveInProc(IntPtr hWaveIn, WIM_Messages msg, IntPtr dwInstance, ref Win32.WAVEHDR wavehdr, IntPtr lParam);
    public delegate void DelegateTimerProc(IntPtr lpParameter, bool TimerOrWaitFired);
    public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);



    Method calls

         /// <summary>
                /// CreateWaveOutHeaders
                /// </summary>
                /// <returns></returns>
                private bool CreateWaveOutHeaders()
                {
                    //Buffer anlegen
                    this.WaveOutHeaders = new Win32.WAVEHDR[BufferCount];
                    this.GCWaveOutHandleBuffers = new GCHandle[BufferCount];
                    GCWaveOutHandleHeaders = new GCHandle[BufferCount];
                    int createdHeaders = 0;

                    //Für jeden Buffer
                    for (int i = 0; i < BufferCount; i++)
                    {
                        //Header erstellen
                        WaveOutHeaders[i].dwLoops = 0;
                        WaveOutHeaders[i].dwUser = IntPtr.Zero;
                        WaveOutHeaders[i].lpNext = IntPtr.Zero;
                        WaveOutHeaders[i].reserved = IntPtr.Zero;

                        //Im Speicher verankern
                        GCWaveOutHandleHeaders[i] = GCHandle.Alloc(this.WaveOutHeaders[i], GCHandleType.Pinned);

                        //Wenn der Buffer vorbereitet werden konnte
                        Win32.MMRESULT hr = Win32.waveOutPrepareHeader(hWaveOut, ref WaveOutHeaders[i], Marshal.SizeOf(WaveOutHeaders[i]));
                        if (hr == Win32.MMRESULT.MMSYSERR_NOERROR)
                        {
                            createdHeaders++;
                        }
                    }

                    //Fertig
                    return (createdHeaders == BufferCount);
                }



             /// <summary>
                /// FreeWaveOutHeaders
                /// </summary>
                private void FreeWaveOutHeaders()
                {
                    try
                    {
                        if (WaveOutHeaders != null)
                        {
                            for (int i = 0; i < WaveOutHeaders.Length; i++)
                            {
                                Win32.MMRESULT hr = Win32.waveOutUnprepareHeader(hWaveOut, ref WaveOutHeaders[i], Marshal.SizeOf(WaveOutHeaders[i]));
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.Write(ex.Message);
                    }
                }


/// <summary>
            /// OpenWaveOuz
            /// </summary>
            /// <returns></returns>
            private bool OpenWaveOut()
            {
                if (hWaveOut == IntPtr.Zero)
                {
                    //Wenn nicht schon offen
                    if (IsWaveOutOpened == false)
                    {
                        //Format bestimmen
                        Win32.WAVEFORMATEX waveFormatEx = new Win32.WAVEFORMATEX();
                        waveFormatEx.wFormatTag = (ushort)Win32.WaveFormatFlags.WAVE_FORMAT_PCM;
                        waveFormatEx.nChannels = (ushort)Channels;
                        waveFormatEx.nSamplesPerSec = (ushort)SamplesPerSecond;
                        waveFormatEx.wBitsPerSample = (ushort)BitsPerSample;
                        waveFormatEx.nBlockAlign = (ushort)((waveFormatEx.wBitsPerSample * waveFormatEx.nChannels) >> 3);
                        waveFormatEx.nAvgBytesPerSec = (uint)(waveFormatEx.nBlockAlign * waveFormatEx.nSamplesPerSec);

                        //WaveOut Gerät ermitteln
                        int deviceId = WinSound.GetWaveOutDeviceIdByName(WaveOutDeviceName);
                        //WaveIn Gerät öffnen
                        Win32.MMRESULT hr = Win32.waveOutOpen(ref hWaveOut, deviceId, ref waveFormatEx, delegateWaveOutProc, 0, (int)Win32.WaveProcFlags.CALLBACK_FUNCTION);

                        //Wenn nicht erfolgreich
                        if (hr != Win32.MMRESULT.MMSYSERR_NOERROR)
                        {
                            IsWaveOutOpened = false;
                            return false;
                        }

                        //Handle sperren
                        GCHandle.Alloc(hWaveOut, GCHandleType.Pinned);
                    }
                }

                IsWaveOutOpened = true;
                return true;
            }
            /// <summary>
            ///Open
            /// </summary>
            /// <param name="waveInDeviceName"></param>
            /// <param name="waveOutDeviceName"></param>
            /// <param name="samplesPerSecond"></param>
            /// <param name="bitsPerSample"></param>
            /// <param name="channels"></param>
            /// <returns></returns>
            public bool Open(string waveOutDeviceName, int samplesPerSecond, int bitsPerSample, int channels, int bufferCount)
            {
                try
                {
                    lock (Locker)
                    {
                        //Wenn nicht schon geöffnet
                        if (Opened == false)
                        {

                            //Daten übernehmen
                            WaveOutDeviceName = waveOutDeviceName;
                            SamplesPerSecond = samplesPerSecond;
                            BitsPerSample = bitsPerSample;
                            Channels = channels;
                            BufferCount = Math.Max(bufferCount, 1);

                            //Wenn WaveOut geöffnet werden konnte
                            if (OpenWaveOut())
                            {
                                //Wenn alle Buffer erzeugt werden konnten
                                if (CreateWaveOutHeaders())
                                {
                                    //Thread starten
                                    StartThreadPlayWaveOut();
                                    IsClosed = false;
                                    return true;
                                }
                            }
                        }

                        //Schon geöffnet
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(String.Format("Start | {0}", ex.Message));
                    return false;
                }
            }
            /// <summary>
            /// PlayData
            /// </summary>
            /// <param name="datas"></param>
            /// <param name="isBlocking"></param>
            /// <returns></returns>
            public bool PlayData(Byte[] datas, bool isBlocking)
            {
                try
                {
                    if (Opened)
                    {
                        int index = GetNextFreeWaveOutHeaderIndex();
                        if (index != -1)
                        {
                            //Werte übernehmen
                            this.IsBlocking = isBlocking;

                            //Daten kopieren
                            GCWaveOutHandleBuffers[index] = GCHandle.Alloc(datas, GCHandleType.Pinned);
                            WaveOutHeaders[index].lpData = GCWaveOutHandleBuffers[index].AddrOfPinnedObject();
                            WaveOutHeaders[index].dwBufferLength = (uint)datas.Length;
                            WaveOutHeaders[index].dwUser = (IntPtr)index;

                            //Abspielen
                            this.IsStarted = true;
                            Win32.MMRESULT hr = Win32.waveOutWrite(hWaveOut, ref WaveOutHeaders[index], (int)Marshal.SizeOf(WaveOutHeaders[index]));
                            if (hr == Win32.MMRESULT.MMSYSERR_NOERROR)
                            {
                                //Wenn blockierend
                                if (isBlocking)
                                {
                                    AutoResetEventDataPlayed.WaitOne();
                                    AutoResetEventDataPlayed.Set();
                                }
                                return true;
                            }
                            else
                            {
                                //Fehler beim Abspielen
                                AutoResetEventDataPlayed.Set();
                                return false;
                            }
                        }
                        else
                        {
                            //Kein freier Ausgabebuffer vorhanden
                            //System.Diagnostics.Debug.WriteLine(String.Format("No free WaveOut Buffer found | {0}", DateTime.Now.ToLongTimeString()));
                            return false;
                        }
                    }
                    else
                    {
                        //Nicht geöffnet
                        return false;
                    }

                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(String.Format("PlayData | {0}", ex.Message));
                    return false;
                }
            }


       /// <summary>
            /// Close
            /// </summary>
            /// <returns></returns>
            public bool Close()
            {
                try
                {
                    lock (Locker)
                    {
                        //Wenn geöffnet
                        if (Opened)
                        {
                            //Als manuel beendet setzen
                            IsClosed = true;
                            Win32.MMRESULT hr = Win32.waveOutReset(hWaveOut);

                            //Warten bis alle Daten fertig abgespielt
                            int count = 0;
                            while (Win32.waveOutClose(hWaveOut) != Win32.MMRESULT.MMSYSERR_NOERROR && count <= 100)
                            {
                                System.Threading.Thread.Sleep(50);
                                count++;
                            }

                            //Variablen setzen
                            IsWaveOutOpened = false;
                            AutoResetEventDataPlayed.Set();
                            return true;
                        }
                        return false;
                    }
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine(String.Format("Close | {0}", ex.Message));
                    return false;
                }
            }
Patrik
  • 1,286
  • 1
  • 31
  • 64
  • I doubt it's an alignment issue, but without seeing more of your code (the setup of the buffers and the P/Invoke declarations) it difficult to tell what's happening. – ctacke Jan 14 '13 at 16:02
  • And what, exactly, is a `WAVEHDR`? You have to give us the *entire* story here. You cannot expect us to guess or try to divine it. You have to help us help you. – ctacke Jan 15 '13 at 01:02
  • I tried to put more code but my current Stackoverflow position won't let me enter more than 34000 characters. – Patrik Jan 16 '13 at 07:06
  • That's *way* more code than I want to walk through. Use the MSDN code pointed to below. I know it works, as I've used it. – ctacke Jan 16 '13 at 13:42

1 Answers1

0

Rather than us trying to debug desktop code that we don't have full source for, have you tried starting with a codebase that is known to work for the Compact Framework instead? There's a complete example of using the waveform audio APIs up in MSDN that probably defines everything you need, and it was written for the Compact Framework.

ctacke
  • 66,480
  • 18
  • 94
  • 155
  • OK. I will rework everything using the pieces of code from the link. But still do no understand what MMSYSERR_INVALIDPARAM means. Does the parameters in the WeaveOutHeader structure are filled with invalid values? At the time of calling the structure is all zeroed but the same works on full .NET framework. – Patrik Jan 14 '13 at 17:00
  • My guess is that your `WaveOutHeaders` is invalid or unsupported, but since I don't know how you've defined it, I can't say for certain. – ctacke Jan 14 '13 at 18:37