Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f84e55
write wavs as 16bit int instead of 32bit float
Oct 25, 2019
5dc5131
update requirements for newer version
Oct 25, 2019
b2155e2
update to tf2 and librosa 0.7.1
Oct 25, 2019
e162cff
first draft for tfio
Oct 28, 2019
fe4df4f
add working tfio loader
Nov 17, 2019
b74f461
add tfio nightly
Nov 18, 2019
b4be19c
update tensorflow benchmark to reflect 2.1/tfio
Dec 6, 2019
0c9a78a
update to tf2.1 and tfio 0.11
Jan 15, 2020
e943535
update loaders
Jan 15, 2020
312116b
remove lib_mad since it is slow and only reads mp3
Jan 15, 2020
b39094a
update torch requirements
Jan 15, 2020
09b93e8
remove mad
Jan 15, 2020
14bce1d
do not print
Jan 15, 2020
ee348ff
libraries not included
Jan 15, 2020
b5c083a
update torchaudio to 0.4.0
Jan 16, 2020
92a728d
also update torchaudio
Jan 16, 2020
4942dea
add remaining libs
Jan 21, 2020
dcf79a7
make plot log scale
Jan 21, 2020
5642f8a
update results
Jan 21, 2020
d71fbe6
update readme
Jan 21, 2020
967bece
ignore through exception
Jan 22, 2020
493d4a4
do not copy numpy data again
Jan 22, 2020
48b9b21
update torchaudio
Jul 22, 2020
ce5929f
update librosa to 0.8.0
Jul 22, 2020
36e9a15
Merge branch 'master' into tf2_librosa7
faroit Sep 6, 2021
0f2ba30
add AUDIO dir
faroit Sep 6, 2021
006d801
update requirements
faroit Sep 6, 2021
50321cc
Merge branch 'tf2_librosa7' of github.com:faroit/python_audio_loading…
faroit Sep 6, 2021
39b0bf1
update codebase
faroit Sep 6, 2021
4cca607
add stempeg
faroit Sep 6, 2021
116927b
add repeat
faroit Sep 6, 2021
b76dbe7
fix tf
faroit Sep 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libavutil-dev \
libswresample-dev \
libfftw3-dev \
libmad0 \
libmad0-dev \
python-gst-1.0 \
python3-gst-1.0 \
libsndfile1 &&\
Expand All @@ -47,7 +45,4 @@ WORKDIR /app

# install requirements, starting with pycairo because it fails in a different order
RUN pip install pycairo
RUN pip install --requirement /app/requirements.txt

# install torchaudio from source
RUN git clone https://github.com/pytorch/audio.git pytorchaudio && cd pytorchaudio && python setup.py install
RUN pip install --requirement /app/requirements.txt
40 changes: 23 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,28 @@ The aim of his repository is to evaluate the loading performance of various audi

This is relevant for machine learning models that today often process raw (time domain) audio and assembling a batch on the fly. It is therefore important to load the audio as fast as possible. At the same time a library should ideally support a variety of uncompressed and compressed audio formats and also is capable of loading only chunks of audio (seeking). The latter is especially important for models that cannot easily work with samples of variable length (convnets).

## Tested Libraries
## Tested Libraries

| Library | Version | Short-Name/Code | Out Type | Supported codecs | Excerpts/Seeking |
|-------------------------|---------|-----------------------|-------------------|-------------------| -----------------|
| [scipy.io.wavfile](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.io.wavfile.read.html#scipy.io.wavfile.read) | 0.14.0 | [`scipy`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L55) | Numpy | PCM (only 16 bit) | ❌ |
| [scipy.io.wavfile memmap](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.io.wavfile.read.html#scipy.io.wavfile.read) | 0.14.0 | [`scipy_mmap`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L61) | Numpy | PCM (only 16 bit) | ✅ |
| [soundfile](https://pysoundfile.readthedocs.io/en/0.9.0/) ([libsndfile](http://www.mega-nerd.com/libsndfile/)) | 0.9.0 | [`soundfile`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L50) | Numpy | PCM, Ogg, Flac | ✅ |
| [scipy.io.wavfile](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.io.wavfile.read.html#scipy.io.wavfile.read) | 1.4.1 | [`scipy`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L55) | Numpy | PCM (only 16 bit) | ❌ |
| [scipy.io.wavfile memmap](https://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.io.wavfile.read.html#scipy.io.wavfile.read) | 1.4.1 | [`scipy_mmap`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L61) | Numpy | PCM (only 16 bit) | ✅ |
| [soundfile](https://pysoundfile.readthedocs.io/en/0.9.0/) ([libsndfile](http://www.mega-nerd.com/libsndfile/)) | 0.9.0 | [`soundfile`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L50) | Numpy | PCM, Ogg, Flac | ✅ |
| [pydub](https://github.com/jiaaro/pydub) | 0.23.1 | [`pydub`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L97) | Python Array | PCM, MP3, OGG or other FFMPEG/libav supported codec | ❌ |
| [aubio](https://github.com/aubio/aubio) | 0.4.9 | [`aubio`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L32) | Numpy Array | PCM, MP3, OGG or other avconv supported code | ✅ |
| [audioread](https://github.com/beetbox/audioread) ([libmad](https://www.underbit.com/products/mad/)) | 2.1.6 | [`ar_mad`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L77) | Numpy Array | FFMPEG | ❌ |
| [audioread](https://github.com/beetbox/audioread) ([gstreamer](https://gstreamer.freedesktop.org/)) |2.1.6 | [`ar_gstreamer`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L67) |2.1.6 | Numpy Array | all of FFMPEG | ❌ |
| [audioread](https://github.com/beetbox/audioread) ([FFMPEG](https://www.ffmpeg.org/)) | 2.1.6 | [`ar_ffmpeg`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L87) | Numpy Array | all of FFMPEG | ❌ |
| [librosa](https://librosa.github.io/) | 0.6.2 | [`librosa`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L104) | Numpy Array | relies on audioread | ✅ |
| [tensorflow 1.13 `contrib.ffmpeg`](https://www.tensorflow.org/api_docs/python/tf/contrib/ffmpeg/decode_audio) | 1.13 | [`tf_decode`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L21) | Tensorflow Tensor | All codecs supported by FFMPEG | ❌ |
| [torchaudio](https://github.com/pytorch/audio) | 0.3.0 | [`torchaudio`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L45) | PyTorch Tensor | all codecs supported by Sox | ✅ |

### Not tested
| [audioread](https://github.com/beetbox/audioread) ([gstreamer](https://gstreamer.freedesktop.org/)) | 2.1.8 | [`ar_gstreamer`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L67) | Numpy Array | all of FFMPEG | ❌ |
| [audioread](https://github.com/beetbox/audioread) ([FFMPEG](https://www.ffmpeg.org/)) | 2.1.8 | [`ar_ffmpeg`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L87) | Numpy Array | all of FFMPEG | ❌ |
| [librosa](https://librosa.github.io/) | 0.7.2 | [`librosa`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L104) | Numpy Array | relies on audioread | ✅ |
| [tensorflow `tf.io.audio.decode_wav`](https://www.tensorflow.org/api_docs/python/tf/contrib/ffmpeg/decode_audio) | 2.1.0 | [`tf_decode_wav`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L22) | Tensorflow Tensor | PCM (only 16 bit) | ❌ |
| [tensorflow-io `from_audio`](https://www.tensorflow.org/io/api_docs/python/tfio/v0/IOTensor#from_audio) | 0.11.0 | [`tfio_fromaudio`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L22) | Tensorflow Tensor | PCM, Ogg, Flac | ✅ |
| [torchaudio](https://github.com/pytorch/audio) (sox) | 0.4.0 | [`torchaudio`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L45) | PyTorch Tensor | all codecs supported by Sox | ✅ |
| [torchaudio](https://github.com/pytorch/audio) (soundfile) | 0.4.0| [`torchaudio`](https://github.com/faroit/python_audio_loading_benchmark/blob/master/loaders.py#L45) | PyTorch Tensor | all codecs supported by Sox | ✅ |
### Not included

* __[audioread (coreaudio)](https://github.com/beetbox/audioread/blob/master/audioread/macca.py)__: only available on macOS.
* __[madmom](https://github.com/CPJKU/madmom):__ same ffmpeg interface as `ar_ffmpeg`.
* __[tensorflow 2 `decode_wav`](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/audio/decode_wav):__ Not released yet.
* __[python builtin `wave`](https://docs.python.org/3.7/library/wave.html)__: TODO
* __[madmom](https://github.com/CPJKU/madmom)__: same ffmpeg interface as `ar_ffmpeg`.
* __[pymad](https://github.com/jaqx0r/pymad)__: only support for MP3, also very slow.
* __[python builtin `wave`](https://docs.python.org/3.7/library/wave.html)__: TODO (open for PR)

## Results

Expand Down Expand Up @@ -73,7 +73,13 @@ Build the docker container using
docker build -t audio_benchmark .
```
It installs all the package requirements for all audio libraries.
Afterwards, mount the data directory into the docker container.
Afterwards, mount the data directory into the docker container and run `run.sh` inside the
container, e.g.:

```bash
docker run -v /home/user/repos/python_audio_loading_benchmark/:/app \
-it audio_benchmark:latest /bin/bash run.sh
```

### Setting up in a virtual environment

Expand Down Expand Up @@ -105,4 +111,4 @@ The data is generated by using a shell script. To generate the data in the folde

## Contribution

We encourage interested users to contribute to this repository in the issue section and via pull requests. Particularly interesting are notifications of new tools and new versions of existing packages. Since benchmarks are subjective, I (@faroit) will reran the benchmark on our server again.
We encourage interested users to contribute to this repository in the issue section and via pull requests. Particularly interesting are notifications of new tools and new versions of existing packages. Since benchmarks are subjective, I (@faroit) will reran the benchmark on our server again.
1 change: 0 additions & 1 deletion benchmark_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ def __len__(self):
'soundfile',
'sox',
'audioread',
# 'pydub', # too slow
]

for lib in libs:
Expand Down
80 changes: 40 additions & 40 deletions benchmark_np.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import matplotlib
matplotlib.use('Agg')
import os
import os.path
import random
import time
import timeit
import argparse
import utils
import loaders
import numpy as np
import functools
import loaders


def get_files(dir, extension):
Expand All @@ -26,24 +24,30 @@ class AudioFolder(object):
def __init__(
self,
root,
download=True,
extension='wav',
lib="librosa",
extension='wav'
):
self.root = os.path.expanduser(root)
self.data = []
self.audio_files = get_files(dir=self.root, extension=extension)
self.loader_function = getattr(loaders, lib)

def __getitem__(self, index):
return self.loader_function(self.audio_files[index])
return self.audio_files[index]

def __len__(self):
return len(self.audio_files)


def test_np_loading(fp, lib):
load_function = getattr(loaders, 'load_' + lib)
audio = load_function(fp)
if np.max(audio) > 0:
return True
else:
return False


if __name__ == "__main__":

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--ext', type=str, default="wav")
args = parser.parse_args()
Expand All @@ -60,44 +64,40 @@ def __len__(self):
# audio formats to be bench
# libraries to be benchmarked
libs = [
'ar_gstreamer',
'scipy',
'stempeg',
'soxbindings',
'ar_ffmpeg',
'ar_mad',
'aubio',
'pydub',
'soundfile',
'librosa',
'scipy',
'soundfile',
'librosa',
'scipy_mmap'
]

for lib in libs:
print("Testing: %s" % lib)
for root, dirs, fnames in sorted(os.walk('AUDIO')):
for audio_dir in dirs:
try:
duration = int(audio_dir)
dataset = AudioFolder(
os.path.join(root, audio_dir),
lib='load_' + lib,
extension=args.ext
)


start = time.time()

for fp in dataset.audio_files:
audio = dataset.loader_function(fp)
np.max(audio)

end = time.time()
store.append(
ext=args.ext,
lib=lib,
duration=duration,
time=float(end-start) / len(dataset),
)
except:
continue
duration = int(audio_dir)
dataset = AudioFolder(
os.path.join(root, audio_dir),
extension=args.ext
)

# for fp in dataset.audio_files:
for fp in dataset.audio_files:
time = min(timeit.repeat(
functools.partial(test_np_loading, fp, lib),
number=3,
repeat=3
))

store.append(
ext=args.ext,
lib=lib,
duration=duration,
time=time,
)

store.df.to_pickle("results/benchmark_%s_%s.pickle" % ("np", args.ext))
40 changes: 25 additions & 15 deletions benchmark_pytorch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ class AudioFolder(torch.utils.data.Dataset):
def __init__(
self,
root,
download=True,
extension='wav',
lib="librosa",
):
Expand All @@ -39,18 +38,19 @@ def __init__(

def __getitem__(self, index):
audio = self.loader_function(self.audio_files[index])
return torch.FloatTensor(audio).view(1, 1, -1)
return torch.as_tensor(audio).view(1, 1, -1)

def __len__(self):
return len(self.audio_files)


if __name__ == "__main__":

parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--ext', type=str, default="wav")
args = parser.parse_args()

repeat = 3
columns = [
'ext',
'lib',
Expand All @@ -63,30 +63,39 @@ def __len__(self):
# audio formats to be bench
# libraries to be benchmarked
libs = [
'ar_gstreamer',
'stempeg',
'soxbindings',
'ar_ffmpeg',
'ar_mad',
'aubio',
'pydub',
'soundfile',
'librosa',
'soundfile',
'librosa',
'scipy',
'scipy_mmap',
]

if args.ext != "mp4":
libs.append('torchaudio')
libs.append('torchaudio-sox_io')
libs.append('torchaudio-soundfile')

for lib in libs:
print("Testing: %s" % lib)
if "torchaudio" in lib:
backend = lib.split("torchaudio-")[-1]
import torchaudio
torchaudio.set_audio_backend(backend)
call_fun = "load_torchaudio"
else:
call_fun = 'load_' + lib

for root, dirs, fnames in sorted(os.walk('AUDIO')):
for audio_dir in dirs:
try:
duration = int(audio_dir)
data = torch.utils.data.DataLoader(
AudioFolder(
os.path.join(root, audio_dir),
lib='load_' + lib,
os.path.join(root, audio_dir),
lib=call_fun,
extension=args.ext
),
batch_size=1,
Expand All @@ -95,18 +104,19 @@ def __len__(self):
)
start = time.time()

for X in data:
X.max()
for i in range(repeat):
for X in data:
X.max()

end = time.time()
store.append(
ext=args.ext,
lib=lib,
duration=duration,
time=float(end-start) / len(data),
time=float(end-start) / (len(data) * repeat),
)
except:
"Error but continue"
continue


store.df.to_pickle("results/benchmark_%s_%s.pickle" % ("pytorch", args.ext))
store.df.to_pickle("results/benchmark_%s_%s.pickle" % ("pytorch", args.ext))
Loading