Win CE(ARM)でWinOut APIを動作させようとしていますが、Windows 7で実行されるのと同じコードを使用して、バッファー準備の行にMMSYSERR_INVALIDPARAMを取得しています。
Win32.MMRESULT hr = Win32.waveOutPrepareHeader(
hWaveOut, ref WaveOutHeaders[i], Marshal.SizeOf(WaveOutHeaders[i]));
if (hr == Win32.MMRESULT.MMSYSERR_NOERROR) ...
これがARMのバイト境界でのバッファアラインメントの問題だと思いますが、.NET Compact Framework forWindowsCEでそれを行う方法がわかりません。
私はいくつかの解決策や説明を見つけようとしていますが、何もありません。
アップデート
これは、WinCEおよび.NETCompactFrameworkで機能するP/Invokeです。インターネット上では、「coredll.dll」の代わりに「winmm.dll」に関連するP/Invokeを見つけることができます。Winmm.dllはWindowsXP、7に存在します。WindowsCEはcoredll.dllのみを動作させます。WinCEで「winmm.dll」を使用すると、「Can't P /Invokewinmm.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);
使用されるデータ構造と定数
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;
}
}