0

If I remove the println! from the following code, waveOutPrepareHeader returns code MMSYSERR_INVALPARAM.

fn write_audio_block(device: &PlaybackDevice, block: &[u8]) {
    use std::mem;
    use std::{thread, time};
    let mut header: mmsystem::WAVEHDR = unsafe { mem::uninitialized() };
    header.dwBufferLength = block.len() as winapi::minwindef::DWORD;
    header.lpData = block.as_ptr() as winapi::winnt::LPSTR;

    println!("ptr: {:?}\nlength: {:?}", // removing this causes error
         header.lpData,
         header.dwBufferLength);

    let header_size: winapi::minwindef::UINT = mem::size_of::<mmsystem::WAVEHDR>() as u32 +
                                           header.dwBufferLength as u32;

    catch_errors(unsafe { winmm::waveOutPrepareHeader(device.handle, &mut header, header_size) });

    catch_errors(unsafe { winmm::waveOutWrite(device.handle, &mut header, header_size) });

    thread::sleep(time::Duration::from_millis(100));

    while unsafe { winmm::waveOutUnprepareHeader(device.handle, &mut header, header_size) } ==
        mmsystem::WAVERR_STILLPLAYING {
        thread::sleep(time::Duration::from_millis(10));
    }
}

Even adding another println! or removing the thread::sleep after waveOutWrite will cause waveOutPrepareHeader to return MMSYSERR_INVALPARAM.

Otherwise it works fine.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user3438815
  • 211
  • 3
  • 12
  • 1
    Why is `block` a `&[u8]`? If you are modifying it, it *needs to be mutable*. If you are going to be writing unsafe code, you need to understand what restrictions you have to uphold. Having an immutable reference and a mutable reference is a sure-fire way to mess things up. – Shepmaster Apr 06 '17 at 13:44
  • where am i modifying block? – user3438815 Apr 06 '17 at 13:59
  • 6
    Welcome to Undefined Behavior; now you understand why Rust being safe 99% of the time is so great! And... we probably won't be able to really help you, unfortunately, without a reproducible example. You may either reduce your problem as a [MCVE] so that we can assist OR use `valgrind` OR try out the [sanitizers](https://users.rust-lang.org/t/howto-sanitize-your-rust-code/9378) OR use a debugger. – Matthieu M. Apr 06 '17 at 13:59
  • Aren't you writing the audio data *into the slice* provided by the caller, modifying the data held within block? Isn't that why you get the pointer with `block.as_ptr()`, store in in your header struct, then pass the header struct to `waveOutWrite` and friends? – Shepmaster Apr 06 '17 at 14:02
  • @MatthieuM. using Valgrind for a Windows FFI problem would be very useful, but I was under the impression that it didn't exist on Windows...? – Shepmaster Apr 06 '17 at 14:03
  • @Shepmaster: Maybe with Windows 10? (I don't know how the new Bash on Windows works exactly). I expect there are equivalent to valgrind? Not quite sure if the sanitizers were ever made to work for Windows unfortunately. – Matthieu M. Apr 06 '17 at 14:07
  • 2
    Why are you *adding* the size of the allocated buffer to the size of the header? That's not the length of the header... – Shepmaster Apr 06 '17 at 14:47
  • 3
    [`WAVEHDR` has **8** fields](https://msdn.microsoft.com/en-us/library/windows/desktop/dd743837(v=vs.85).aspx), of which you are only setting **2**. All the others are random junk memory. Instead of `mem::uninitialized` (which is documented as "This is incredibly dangerous and should not be done lightly"), try setting *all* the values to some reasonable initial value. My bet is that `println` just happens to clear some memory that the uninitialized variable is using. – Shepmaster Apr 06 '17 at 14:52
  • 1
    Indeed, in C (where `uninitialized` is the default) you'd usually memset-0 a structure to make sure it doesn't have the random stuff left. cf. http://stackoverflow.com/questions/10788861/is-memsetmystruct-0-sizeof-mystruct-same-as-mystruct-0; http://stackoverflow.com/questions/6891720/initialize-reset-struct-to-zero-null – ArtemGr Apr 06 '17 at 15:35
  • @Shepmaster: maybe `mem::zeroed()`? – Matthieu M. Apr 06 '17 at 16:34
  • @MatthieuM. I thought about that too, but it depends on the various values of the header allowing 0 (or equivalent) as a valid value. It's probably just better to go ahead and explicitly initialize each input field, reading the documentation to understand what everything needs to be set to. After that, maybe zeroing it out makes sense. – Shepmaster Apr 06 '17 at 16:37

0 Answers0