dancasimiro/WAV.jl

wavplay for Windows: what needs to be done?

Closed this issue · 4 comments

Hello. It would be great if wavplay works on windows machines just like on Macs and linux boxes. In README.md, it says that there is not a native backend for windows yet, but what needs to be done here? MATLAB can play audio files on windows, but I really want to use julia instead.
Thanks!
BVPs

You need to implement wavplay(data, fs) for windows. The pulse implementation is simpler than the audio queue version. Then, you need to add a runtime check into __init__() to load the windows code.

I don't use Windows, so I can't help with the windows specific audio code. I can help out if you have questions about the data formats or runtime checks. I checked Google quickly, and I can't tell if pulse audio supports Windows. The existing pulse code should work on Windows if the library indeed supports the OS.

You are free to choose any audio interface that works on Windows.

According to the following page, it seems that pulseaudio has been ported to the Windows environment although it may not be with full functionality: https://www.freedesktop.org/wiki/Software/PulseAudio/
So, I really hope someone can implement things for Windows!

One main advantage of this library over LibSndFile is that it is a pure Julia implementation that has no external dependencies on C libraries beyond what comes with a standard OS install. Therefore, while using PulseAudio on Linux is fine, because it is widely installed there already by default, the same can't be claimed at all for Windows.

Here is the simplest solution I can think of:

using WAV

# some standard Win32 API types and constants, see [MS-DTYP]
const BOOL = Cint
const DWORD = Culong
const TRUE = 1
# PlaySound flags from Winmm.h
const SND_SYNC      = 0x0
const SND_ASYNC     = 0x1
const SND_NODEFAULT = 0x2
const SND_MEMORY    = 0x4
const SND_FILENAME  = 0x20000

function wavplay(y, fs)
    # produce an in-memory WAV file ...
    buf=IOBuffer()
    wavwrite(y, buf, Fs=fs)
    wav = take!(buf)
    # ... and pass it to PlaySound
    success = ccall((:PlaySoundA, "Winmm.dll"), stdcall, BOOL,
                    (Ptr{Cvoid}, Ptr{Cvoid}, DWORD),
                    wav, C_NULL, SND_MEMORY | SND_SYNC | SND_NODEFAULT)
    Base.windowserror("PlaySound", success != TRUE)
end

fs = 8000
f = 400
t = 0:1/fs:prevfloat(1.0)
y = sin.(2pi * t * f)
wavplay(y, fs)

It does copy the waveform data around, which is not super elegant, but I doubt the performance penalty is measurable at audio sampling rates.

One alternative would be the waveOut API (waveOutOpen, waveOutPrepareHeader, waveOutWrite, waveOutUnprepareHeader, waveOutClose). But some more care is needed with regard to setting up sampling rate and data format correctly (which I haven't fully understood yet), whereas handing over a WAV file does all that using existing mechanics.

If the above sounds acceptable, I'd be happy to prepare a PR (but please have a look first at my previous one: #86).

Note that PlaySound can also play files directly.

Hi @mgkuhn ! That suggestion looks like a great solution. I would be happy to merge a PR based on your proposal.