How to play an audio in Julia?

I use this small utility function to play audio I have in memory, may be useful to someone, replace totem for your audio player of choice

using WAV
function WAV.wavplay(x::AbstractArray,fs)
    path = "/tmp/a.wav"
    wavwrite(x,path,Fs=fs)
    @info "Wrote to file $path"
    Threads.@spawn run(`totem $path`)
end
6 Likes

While LibSndFile has been updated for 1.3, PortAudio is still in-progress. There are a number of issues with binary packaging that I’ve been working through, but haven’t had time to get it fully wrapped up. It should more-or-less work on the julia1 branch (do pkg> add PortAudio#julia1 but it’s been in-progress for a while.

If you use the Atom editor with Juno (which I recommend in general because it’s a really nice experience for editing/running julia code), then LibSndFile will display as playable audio:

I think you should get something similar if you use Jupyter, though I haven’t tested.

On Linux and OSX, WAV.jl has some functionality to do playback, but I haven’t used it.

5 Likes

it seems to be unmaintained… i.e. there is a long standing issue about playback that has had no attention

Yeah, progress has been sporadic, though things have moved along a bit. Now folks can use master rather than a random branch, and it also uses the new BinaryBuilder-based C binaries. Which issue are you referring to?

There are still some segfaults I need to work through and some build-system issues to add support for various audio backends (most notably PulseAudio devices don’t seem to be recognized by the ALSA backend for some reason).

I was meaning this one:

is there any work around?

also, thanks for the tip about LibSndFile, it works great!

I think I spoke too soon: there was a recompile, and now the example you gave above doesn’t produce a sound player in Juno anymore. I instead get some type information:

Also, I checked and it doesn’t produce a player in jupyter.

would be great to see something like this folded into WAV

On Linux with PulseAudio I managed to play a function. Somehow the import is broken. Maybe it helps.

using WAV

include("~/.julia/packages/WAV/T2P9V/src/wavplay-pulse.jl")

t = 0.5
fs = 8000
x = 1:fs*t

f(f) = @. (sin(2*pi/fs*f*x)+sin(2*pi/fs*f*1.26*x)+sin(2*pi/fs*f*1.34*x))

s =  vcat(f.(rand([
523.251,
493.883,
466.164,
440.000,
415.305,
391.995,
369.994,
349.228,
329.628,
311.127,
293.665,
277.183,
261.626 ],100))...)
wavplay(s,fs)

You’ll have to uninstall WAV.jl if you want load to use LibSndFile instead, the issue is that the FileIO package prefers to use WAV so it will use that instead if it’s installed.

WAV is a separate package with a different maintainer from LibSndFile. WAV is a pure-Julia implementation of WAV file processing. LibSndFile can handle more file types but relies on a C library to do so.

Also, in case it’s not clear - I don’t maintain or use WAV.jl, it’s maintained by @dancasimiro. In addition to WAV file support it can play and record audio.

I maintain LibSndFile.jl and PortAudio.jl, which are separate packages for working with audio devices and files, respectively.

Besides the pure-Julia vs. binary library aspect, WAV.jl provides a more matlab-familiar interface and returns a tuple of an Array with the audio data and a sampling rate. LibSndFile returns a SampleBuf object, which is a wrapper around a raw array that includes the sample rate and some convenience functionality, like the widget display in Juno and Jupiter. I think WAV.jl has something similar, though maybe you need to call a function to show the widget.

2 Likes

thanks a lot for this. one question: since libsndfile.jl does what I need and works, how would i pull out the raw data values from a SampleBuf?

Can LibSndFile.jl save a .wav if WAV is not installed? I seem to be able to play files, but not save them when WAV is removed… so then the readme examples don’t work

You can do buf.data to get the underlying array from a samplebuf.

Libsndfile supports WAV files and doesn’t depend on WAV.jl.

If the readme example doesn’t work can you file an issue with the error you’re seeing?

thanks for this. I think more an issue with my understanding… the issue is more that I want an equivalent of WAV’s

y, fs, nbits, extra = wavread(“sound.wav”)

y2 = some processing of raw data via fft, filter, etc

wavwrite(y2, fs, 32,“sound2.wav” )

I can’t see from LibSndFile how to process the raw data and then save to .wav again. Maybe type conversion to FixedPointNumber ?

Yeah, I think the current design makes that harder than it needs to be. You’re correct that currently the way to do it is to convert to FixedPoint values before saving (assuming that your processing is giving your floats). Originally the idea was that I didn’t want LibSndFile to do any automatic conversion, so if you read a WAV file and then wrote the contents back to disk it would be a lossless process. In practice you are almost always end up wanting to work with floats so the defaults should probably reflect that.

See this issue with some of my thoughts on it:

Sorry for all of the trouble with wavplay in WAV.jl. The mechanism that I used to detect libraries at runtime has broken over time. I could revert the complexity and always depend on the presence of pulse audio.

2 Likes

If you just want to play it, you can use Shell-command interpolation instead:

run(`vlc file.wav -Idummy`)

(Idummy runs VLC without GUI)

1 Like

If I add “using MFCC” below then I no longer see the sound player, does its use of WAV internally somehow override? I am able to convert to a sampleBuf and then I see the player but I cannot play anything lower than at 3k samplerate, not sure this is a bug or a limitation of the OSX audio engine player through juno.

The audio libraries are in much better shape now than when this question was asked originally. For people like me who are migrating from Matlab and might find this discourse discussion, there is a simple sound function in this package:
GitHub - JeffFessler/Sound.jl: Provide `sound` function for playing audio.
It is currently just a wrapper around PortAudio functions.

6 Likes

the guys could put this feature in vscode.