msndfile is a suite of MATLAB Mex wrappers of the libsndfile C library for reading and writing audio files.

This project is split between SourceForge.net and github.com. The SF.net project page hosts downloads, this website and the git repository. The github page also hosts the git repository (isn’t git nice?) and a bug tracker and is basically where development happens.

1. Introduction

The MATLAB built-in functions for reading and writing WAV files (i.e., wavread and wavwrite) have their limitations. They do not support files larger than 4 GB, and do not support certain non-Microsoft WAV formats (e.g., RF64). This leads to users editing wavread to support whatever formats they need to use. This project, msndfile, aims to provide wavread and wavwrite compatible functions using the libsndfile C library, which supports various formats, including many WAV variants, and has a track record of robustness
[I received informal feedback from one user who had WAV files that wavread() had trouble reading, but which libsndfile handled fine.]
. This also means that msndfile supports Ogg Vorbis and (in my opinion much more interesting) the Free Lossless Audio Codec FLAC.

Currently, the suite consists of two functions: msndfile.read and msndfile.blockread. The latter is intended solely for reading audio files block-wise, for instance to process large files that do not fit in RAM. Still missing are the corresponding functions for writing data: msndfile.write and msndfile.blockwrite. Just as with msndfile.read, msndfile.write is supposed to be API-compatible with wavwrite.

2. Features

The msndfile suite currently has the following features:

  • Can read any file type supported by libsndfile, such as FLAC and Ogg Vorbis, but also various WAV formats not supported by the wavread function, e.g. Riff64 (RF64), and has no (known) file size limit.

  • Supports BWF metadata (the bext chunk).

  • Can read multiple files in a block-wise manner via the function msndfile.blockread.

  • It is in general more efficient (read: faster) than pure-MATLAB code, except under special circumstances which favour the JIT compiler
    [The one example I can think of is a pure-MATLAB function for block-wise reading of WAV files that becomes extremely fast when reading the same range of samples repeatedly.]
    .

3. Compatibility with wavread

While the goal of msndfile.read is to be API-compatible with wavread, there are special cases where this cannot be (completely) achieved. These are detailed in the following sections.

3.1. Leaving off the file type extension

While msndfile supports reading files without the file type extension, i.e.

>> msndfile.read('some_file');

works, the following conditions must be met:

  • There may be no ambiguity with regards to which file is meant, unless one of the files with the same basename is a WAV file. For example: if you try to read some_file and there are two files, some_file.aiff and some_file.flac, msndfile will abort with an error. However, if there is also a file some_file.wav, msndfile will read that.

  • The format must be what the libsndfile documentation refers to as “simple”. These “simple formats” are: AIF{F,C}, AU, CAF, FLAC, VOX, Ogg and WAV. Additionally, msndfile explicitly supports RAW files.

For other, more “exotic” file types, it is therefor necessary to provide the file type extension.

3.2. Differences when the file is not a WAV file

Msndfile may differ in behaviour a bit if a file is not a WAV file. In these cases, the following differences to wavread hold:

  • The opts.fmt struct has no “wFormatTag” field if the file type is not a WAV file (which is completely logical).

  • Broadcast WAV files produce an additional opts.bext field.

  • The opts.info struct may contain fields unique to non-WAV formats.

  • RAW files require an additional argument: a struct detailing the file format.

3.3. Warnings and errors

Msndfile will of course have its own unique warnings and errors, but might also print different warnings and errors in identical situations. This might be less of a problem in situations where the error is a system error, in which case it seems that wavread and msndfile both print the system error (i.e., strerror(errno)).

More importantly, though, the error and warning IDs will be different, since msndfile uses its own ID component msndfile:, as per MATLAB documentation
[If requested, I could at least make the mnemonics identical to wavread]
.

3.4. Changes in the behaviour of wavread

It may be that some wavread() behaviour may change over time or may have changed in the (recent) past. Msndfile will for simplicity only implement one type of behaviour. Here is a list of all known examples:

  • In older versions of MATLAB trying to read zero samples will instead read a file in its entirety. Newer versions return an empty matrix, which is what msndfile does. Note that some versions of MATLAB (e.g., 2010a) print a large warning that the behaviour will change.

4. Download

The most recent version of msndfile is 1.1. You can download the source package, binaries (32 bit Windows, 32 bit and 64 bit Linux) and a PDF version of this website here:

The 32 bit binaries were compiled using MATLAB 2010a Student Edition, the 64 bit Linux binaries using 2013a Student Edition.

Older versions can be found at the SourceForge project page. The Windows binaries bundle the libsndfile DLL. Under Linux and Mac it is expected that libsndfile is already installed (e.g., via a package manager).

5. Source code repository

The msndfile source code is managed with Git. The repository is hosted on github, see the github page. If you would like to contribute (be it with bug reports or patches), this is the place to go. The repository is also hosted on SourceForge.net for redundancy, see the project page.

6. Installing msndfile from source

There are two ways to compile msndfile. The easy way is to call the m-file compile_msndfile.m from MATLAB. This is probably the way to go if you only want to use msndfile, but none of the binary downloads satisfy your needs. There is also a more flexible build system based on SCons available, that can also generate a Visual Studio IDE project file and can be integrated into other IDEs like Eclipse. You will most likely want to use this if you want to work on msndfile. For details on both methods, see their corresponding subsections.

6.1. Prerequisites

The basic dependencies of msndfile are:

  • MATLAB (tested with 2010a Student Edition)

  • libsndfile

  • a C compiler that supports C99 (e.g., GCC, clang)

  • stdint.h (a C99 header file; required for reading files in their native format, à la wavread(..., 'native'))

  • MTest (needed only for the test suite)

The requirement for a compiler that supports C99 means that the Microsoft Visual Compiler cannot be supported anymore. However, you can still generate Visual Studio projects with the SCons based build system; they will simply use another compiler
[I do not know if debugging in VS is possible this way; it might be with the appropriate add-ins, e.g., VisualGDB.]
. If stdint.h is not available (as might be the case for older compilers), a compatibility header (stdint_compat.h) is used instead.

To obtain libsndfile, you may try the following steps:

Linux

Install the libsndfile development package via your distributions package manager (typically a package with a -devel or -dev suffix).

Windows

Follow the instructions at http://www.mega-nerd.com/libsndfile). After you have done that, copy the files libsndfile-1.dll, libsndfile-1.lib and sndfile.h into the Win subdirectory.

Mac OS X

Either download the sources from http://www.mega-nerd.com/libsndfile and compile them yourself or use a package management system like fink or macports or whatever system you already use.

If you are going to use the SCons based build system, you will also need

The MATLAB Tool is a submodule of this repository. To install it, just run

$ git submodule update --init

Finally, if you want to build the documentation, you need these additional dependencies:

  • AsciiDoc (and its dependencies, which can be vast, depending on the output format — it is probably easiest to do this on a Linux system)

  • the SCons AsciiDoc Tool

The SCons Tool is, again, a submodule of this repository and will be installed by running the above git command.

6.2. Using the compile_msndfile.m script

In MATLAB, type compile_msndfile to compile all Mex extensions. By default this will put the binaries in the +msndfile subdirectory (a Matlab package directory). See the built-in help (help compile_msndfile) for overriding the defaults to your needs.

Note

If mex -setup does not find a C99 compatible compiler, you will have to configure mex manually. The options I know of are:

  • either create your own mexopts.bat/mexopts.sh file, or

  • use MinGW/Cygwin with gnumex.

6.3. Using the SCons based build system

The SCons based build system is in general more robust and flexibel than calling Mex in compile_msndfile.m. It uses an extension that properly presets some environment variables, though it slows compilation down the first time it is run because it starts a MATLAB subprocess to obtain them.

Anyway, to use SCons, call it from the command line like so:

$ scons [--interactive] [options] [target]

As to why I wrote a complicated SCons
[A CMake based build system was also attempted, but CMake proved difficult to extend, at least in comparison with SCons.]
build system in the first place, well… Apart from the usual features of real build systems (proper dependency tracking, parallel builds, platform dependent configuration à la autoconf, etc.) it can do the following things:

  • create a Visual Studio solution file with Release and Debug Variants

  • create a ZIP file for distribution

  • separates release from debug builds

  • could support cross-compiling to other architectures/operating systems to provide binaries for them

  • builds the AsciiDoc documentation you are currently reading

  • supports whatever compilers SCons (or third party extensions) supports, and not just what The Mathworks supports, which is incomplete and quite outdated anyway
    [Fun fact: the most recent minor version of GCC officially supported by The Mathworks for MATLAB 2012a (GCC 4.4) is not even supported by the compiler vendor anymore, though I suppose this may be deliberate.]
    .

For more information (such as available build targets, options and overridable environment variables), see the output of scons --help.

If you need to work on the code and plan to compile often, you can speed up the process in general by using the interactive mode by passing the --interactive option to SCons.

Note
To debug from within an IDE, you need to be sure to call MATLAB with the -wait argument.

6.3.1. Linux

Just open a terminal, navigate to the top-level project directory, and type

$ scons

6.3.2. Windows

You can launch the batch file start_scons.bat, which launches scons and keeps the terminal window open. It is expected that SCons is in your $PATH.

Under Windows, the build system has an additional vsproj target. This creates an MS Visual Studio Solution file with msndfile as a project. For other IDEs, see http://www.scons.org/wiki/IDEIntegration.

6.3.3. Mac OS X

Follow the same steps as under Linux, or look into integrating SCons into your IDE of choice (see http://www.scons.org/wiki/IDEIntegration).

6.4. Testing

Msndfile comes with a comprehensive test suite (based on MTest) that is run by executing test_msndfile.m from MATLAB. There are also performance evaluation scripts, however they take several minutes and so are not run by default. To run them, too, set do_perf_tests to true in test_msndfile.m.

Note
If you use SCons, you must run the install target first and make sure DESTDIR is in MATLABs path.
Note
To run the bext chunk test you need the file EBU_sample.wav , which I found here. Put it in the test_files/bwf/ subdirectory.

7. Examples

Here are some examples demonstrating msndfile.read and msndfile.blockread.

Note
See the help text (help msndfile.read and help msndfile.blockread) for the full documentation, including all available options.

7.1. msndfile.read

First some API compatibility examples:

For starters, we verify that the 'size' command yields identical results. First wavread:

Example 1. Output of wavread(..., 'size')
EDU>> [dim, fs, nbits, opts] = wavread('test_files/test.wav', 'size');
EDU>> dim
dim =
      418853           2
EDU>> fs
fs =
       44100
EDU>> nbits
nbits =
    16
EDU>> opts
opts =
     fmt: [1x1 struct]
    info: [1x1 struct]
EDU>> opts.fmt
ans =
         wFormatTag: 1
          nChannels: 2
     nSamplesPerSec: 44100
    nAvgBytesPerSec: 176400
        nBlockAlign: 4
     nBitsPerSample: 16
EDU>> opts.info
ans =
    inam: 'glass cafe'
    iart: 'Jon 7'
    icrd: '2006'

Now msndfile.read:

Example 2. Output of msndfile.read(..., 'size') — WAV file
EDU>> [dim, fs, nbits, opts] = msndfile.read('test_files/test.wav', 'size');
EDU>> dim
dim =
      418853           2
EDU>> fs
fs =
       44100
EDU>> nbits
nbits =
    16
EDU>> opts
opts =
     fmt: [1x1 struct]
    info: [1x1 struct]
EDU>> opts.fmt
ans =
         wFormatTag: 1
          nChannels: 2
     nSamplesPerSec: 44100
    nAvgBytesPerSec: 176400
        nBlockAlign: 4
     nBitsPerSample: 16
EDU>> opts.info
ans =
    inam: 'glass cafe'
    iart: 'Jon 7'
    icrd: '2006'

And for comparison the same command, but with a FLAC file:

Example 3. Output of msndfile.read(..., 'size') — FLAC file
EDU>> [dim, fs, nbits, opts] = msndfile.read('test_files/test.flac', 'size');
EDU>> dim
dim =
      418853           2
EDU>> fs
fs =
       44100
EDU>> nbits
nbits =
    16
EDU>> opts
opts =
     fmt: [1x1 struct]
    info: [1x1 struct]
EDU>> opts.fmt
ans =
          nChannels: 2
     nSamplesPerSec: 44100
    nAvgBytesPerSec: 176400
        nBlockAlign: 4
     nBitsPerSample: 16
EDU>> opts.info
ans =
    inam: 'glass cafe'
    iart: 'Jon 7'
    icrd: '2006'
    ialb: 'Sound Without Film [JON7NET015]'
    inum: '1'
    ignr: '(12)'

Notable differences are:

  • opts.fmt lacks the field wFormatTag (but is otherwise identical)

  • opts.info contains more meta-data tags

Next we read the first and last ten samples. Again, first wavread:

Example 4. Reading the first and last ten samples — wavread()
EDU>> wavread('test_files/test.wav', 10)
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
EDU>> wavread('test_files/test.wav', [1 10])
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
EDU>> wavread('test_files/test.wav', [dim(1)-9 dim(1)])
ans =
   1.0e-03 *
         0         0
   -0.1221    0.0610
   -0.1221    0.0305
   -0.0916    0.0305
   -0.0916    0.0305
   -0.0610   -0.0305
    0.0305   -0.1221
    0.1221   -0.1221
    0.0305         0
         0         0

Now msndfile.read:

Example 5. Reading the first and last ten samples — msndfile.read()
EDU>> msndfile.read('test_files/test.wav', 10)
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
EDU>> msndfile.read('test_files/test.wav', [1 10])
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
EDU>> msndfile.read('test_files/test.wav', [dim(1)-9 dim(1)])
ans =
   1.0e-03 *
         0         0
   -0.1221    0.0610
   -0.1221    0.0305
   -0.0916    0.0305
   -0.0916    0.0305
   -0.0610   -0.0305
    0.0305   -0.1221
    0.1221   -0.1221
    0.0305         0
         0         0

And as a last comparison, the first ten samples read with the 'native' option:

Example 6. Reading the first and last ten samples with the native option
EDU>> wavread('test_files/test.wav', [1 10], 'native')
ans =
     -1      0
      0      0
      1     -2
      3     -5
      4     -3
      3     -3
      1     -2
      1      0
     -1     -2
      2     -4
EDU>> msndfile.read('test_files/test.wav', [1 10], 'native')
ans =
     -1      0
      0      0
      1     -2
      3     -5
      4     -3
      3     -3
      1     -2
      1      0
     -1     -2
      2     -4

And now, to show that using 'native' does not always make sense, here the output using a FLAC file:

Example 7. Using msndfile.read(..., 'native') with a FLAC file
EDU>> msndfile.read('test_files/test.flac', 10, 'native')
ans =
  -8056 -25196
   9239   5111
  27485 -26301
   3763  12444
  -4953  20257
  32212   8889
   -368  -2329
  15185   4621
  11586   6946
   9991  21003

A unique feature of msndfile.read is support for RAW files. Here is an example showing how to set up the necessary struct.

Example 8. Using msndfile.read(...) to read a RAW file

You need to define a structure with at least three fields: samplerate, channels and sampleformat. If either of these is not set, msndfile.read will output an error.

EDU>> file_info.samplerate = 44100;
EDU>> file_info.channels = 2;
EDU>> msndfile.read('test_files/test.raw', 10, [], file_info)
??? Error using ==> read
Field 'sampleformat' not set.

Now with all required fields set:

EDU>> file_info.sampleformat = 'PCM_16';
EDU>> msndfile.read('test_files/test.raw', 10, [], file_info)
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.122

Of course 'native' works here, too:

EDU>> msndfile.read('test_files/test.raw', 10, 'native', file_info)
ans =
     -1      0
      0      0
      1     -2
      3     -5
      4     -3
      3     -3
      1     -2
      1      0
     -1     -2
      2     -4

7.2. msndfile.blockread

Here are some examples showing the use of msndfile.blockread.

First we open a file and read 10 samples twice in a row:

Example 9. Basic usage of msndfile.blockread — open and read commands
EDU>> msndfile.blockread('open', 'test_files/test.wav');
EDU>> msndfile.blockread('read', 'test_files/test.wav', 10)
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
EDU>> msndfile.blockread('read', 'test_files/test.wav', 10)
ans =
   1.0e-03 *
    0.0916   -0.1526
    0.0916   -0.1526
    0.0916   -0.1526
    0.0610   -0.1526
    0.0916   -0.1831
    0.0610   -0.1221
   -0.0305   -0.0305
   -0.1221         0
   -0.1221   -0.0610
         0   -0.2136

Now let us seek back to the beginning and read the first 20 samples again.

Example 10. Basic usage of msndfile.blockread — seek and tell commands
EDU>> msndfile.blockread('seek', 'test_files/test.wav', 1)
EDU>> msndfile.blockread('tell', 'test_files/test.wav')
ans =
     1
EDU>> msndfile.blockread('read', 'test_files/test.wav', 20)
ans =
   1.0e-03 *
   -0.0305         0
         0         0
    0.0305   -0.0610
    0.0916   -0.1526
    0.1221   -0.0916
    0.0916   -0.0916
    0.0305   -0.0610
    0.0305         0
   -0.0305   -0.0610
    0.0610   -0.1221
    0.0916   -0.1526
    0.0916   -0.1526
    0.0916   -0.1526
    0.0610   -0.1526
    0.0916   -0.1831
    0.0610   -0.1221
   -0.0305   -0.0305
   -0.1221         0
   -0.1221   -0.0610
         0   -0.2136
EDU>> msndfile.blockread('tell', 'test_files/test.wav')
ans =
    21

The tell command returns the current read position of the file, i.e., where the next read would occur if you do not pass the start position.

Now that we are done, we can close the file:

Example 11. Basic usage of msndfile.blockread — close command
EDU>> msndfile.blockread('close', 'test_files/test.wav')
EDU>> msndfile.blockread('close', 'test_files/test.wav')
??? Error using ==> blockread
File not open.

As you can see, attempting to close a file twice yields an error.

Now the final example: for compatibility with wavread, both msndfile.read and msndfile.blockread must internally transpose the output. For a small performance boost, you can turn that off by passing a fourth argument to msndfile.blockread.

Example 12. Not transposing the output
EDU>> msndfile.blockread('read', 'test_files/test.wav', 10, false)
ans =
   1.0e-03 *
  Columns 1 through 9
    0.0610    0.0305         0    0.0305    0.0610         0   -0.0305    0.0305   -0.0305
   -0.2136   -0.1831   -0.2136   -0.2441   -0.2441   -0.2136   -0.2136   -0.2747   -0.1526
  Column 10
   -0.1526
   -0.0610
Note
To see the effect of the affect on performance, run the performance tests (see [testsuite]).

8. Bugs, feature requests & patches

Bugs and features should ideally be reported/requested on the github page (using the issue tracker) or via marcec@gmx.de. Patches should ideally be handled via pull requests (either via the github page or by email), but I will also accept patches sent via email.

9. Credits

msndfile profits from the use of various other open-source projects. These are credited here.

AsciiDoc

This page was created using AsciiDoc, a great format for writing documentation.

MTest

The test suite is implemented using MTest, the “MATLAB xUnit Test Framework”.

./images/valgrind-link1.png

The Valgrind memory debugger proved useful in tracking down memory allocation bugs.

./images/SCons-Bricks.png

SCons proved a flexible and relatively easy to use — and extend — build system.

./images/git-logomark-orange.png

Git is simply an awesome version control system.

Also, thanks to Bastian Bechtold for his feedback on this website and for testing msndfile under Mac.

Appendix A: Copyrights

msndfile is Copyright © 2010-2013 Marc Joliet <marcec@gmx.de> and licenced under the MIT licence. See the file LICENSE in the project repository.

The documentation of msndfile (the AsciiDoc sources of this site and the file README.md) is Copyright © 2010-2013 Marc Joliet <marcec@gmx.de> and licenced under the Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) license.

The SCons logo is licensed under the Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported license.

The Git Logo by Jason Long is licensed under the Creative Commons Attribution 3.0 Unported license.

The test files test.{raw,flac,wav} are an excerpt of “Glass Cafe” by Jon7 and are licensed under the Attribution-NonCommercial-ShareAlike 2.5 Generic (CC BY-NC-SA 2.5) license.

libsndfile is maintained by Erik de Castro Lopo <erikd@mega-nerd.com> and is licensed under the LGPLv2.1 (included in the source directory in the file “LGPL-2.1”).

Appendix B: Document revision history

Version

Date

Summary of changes

1.0

2012.11.05

First version.

1.0.1

2012.11.05

Add credit to Git and MTest, miscellaneous edits/improvements, update copyrights.

1.0.2

2012.11.30

Update for 1.0 release. Lot’s of improvements and updates.

1.1.0

2013.11.30

Update for 1.1 release. Lot’s of improvements and updates.

1.1.1

2014.01.22

Provide link to 64 bit Linux binaries.