Lunin
Lunin

Reputation: 370

waveOutPrepareHeader returns INVALPARAMS

I've been trying to track this down for a while but I'm at a loss as to where to look next. Whenever I call waveOutPrepareHeader() I get INVALPARAMS indicating "The buffer's base address is not aligned with the sample size."

I am currently preparing my header from the data after the "data" tag (and length) from the file using the method found via MSDN.

    public Wave.SystemError Read(BinaryReader rdr, uint readLength)
    {
        dwBufferLength = readLength;
        byte[] data = new byte[readLength];
        rdr.Read(data, 0, data.Length);

        if (lpData == IntPtr.Zero)
            lpData = Memory.LocalAlloc(Memory.LMEM_FIXED,
               (uint)data.Length);

        if (lpData == IntPtr.Zero)
            return Wave.SystemError.NOMEM;

        Marshal.Copy(data, 0, lpData, data.Length);

        return Wave.SystemError.NOERROR;
    }

All the parameters of the class are zeroed out before use, after calling this function on a wave file I get

dwBufferLength = 32768
dwBytesRecorded = 0
dwFlags = 0
dwLoops = 0
dwUser = 0
lpData = 384656
lpNext = 0
reserved = 0

when passed into

return waveOutPrepareHeader(hWaveOut, 
                            ref headerBuffer[buffIndex],
                            (uint)Marshal.SizeOf(headerBuffer[buffIndex]));

where headerBuffer[buffIndex] is the abovementioned WAVEHDR I get 11 (MMSYSERR_INVALPARAM). I've checked and my hWaveOut and size seems to be sane so I'm forced to conclude the problem is with the header but I can't figure out what is wrong or even what to check.

What do I need to do to fix this error, or failing that, what can I check to see what is causing it? Any assistance would be greatly appreciated

Upvotes: 2

Views: 1347

Answers (2)

Lunin
Lunin

Reputation: 370

The problem ended up being not an issue with lpData (which was being allocated properly by LocalAlloc), but an issue with the way that the WAVEHDR structure itself was being passed to the PInvoked waveOutPrepareHeader.

Most material on waveOut (even for C#) has you passing in an object of the header's type. But in order to stop getting the error, I had to make a block of data using Marshal.AllocHGlobal and copy the header into it with Marshal.StructureToPtr. The IntPtr from the AllocHGlobal then is passed into waveOutPrepareHeader as such and that resolved the issue.

Here's some rough sample code for any who run up against a similar problem.

WAVEHDR header = new WAVEHDR();
//Read or setup header data here
IntPtr headerPtr = Marshal.AllocHGlobal(Marshal.SizeOf(header));
Marshal.StructureToPtr(header, headerPtr, true);
waveOutPrepareHeader(hWaveOut, headerPtr, (uint)Marshal.SizeOf(header));

This assumes that you set your PInvoke for everything that uses the header to an IntPtr type instead of a WAVEHDR one.

From there, use the headerPtr for all the places where you need to pass in that header. You'll have to handle the freeing of memory yourself at the appropriate location as you would expect.

Upvotes: 1

marcinj
marcinj

Reputation: 49976

Is your sample size bigger than 1 byte? If so then ...

on ARM it is important to keep data aligned, so if you allocate BYTE (one byte) buffer and later cast it to SHORT (two bytes), then if BYTE buffer was allocated on odd address then you will get unaligned data access.

So if your sample size is two bytes then you must ensure that your BYTE buffer address is always even.

To verify this you can check what value does lpData have when error appears, if it is odd then simply allocate 1 byte more and send buffer+1 to winapi function.


below is sample code with proper alignment, found at: http://www.utdallas.edu/~nsg051000/AmbientNoise4/Wave.cs

    ///  
    /// Read a data buffer from the supplied BinaryReader.  This method will 
    /// allocate memory for the data buffer it is not already allocated. 
    ///  
    /// BinaryReader containing data 
    /// Size, in bytes, to be read 
    /// MMSYSERR.NOERROR if successful 
    public MMSYSERR Read(BinaryReader rdr, uint readLength, int align)
    {
        uint bufferLength = readLength;

        if (bufferLength % align != 0)
            bufferLength += (uint)(align - (bufferLength % align));

        dwBufferLength = bufferLength;
        byte[] data = new byte[readLength];
        rdr.Read(data, 0, data.Length);

        if (lpData == IntPtr.Zero)
            lpData = Memory.LocalAlloc(Memory.LMEM_FIXED, (uint)bufferLength);

        if (lpData == IntPtr.Zero)
            return MMSYSERR.NOMEM;

        Marshal.Copy(data, 0, lpData, data.Length);

        return MMSYSERR.NOERROR;
    }

Upvotes: 2

Related Questions