From 1eebbc746f715e32f165ed62fdc57447a5903b21 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 7 Jul 2009 08:58:51 +0200 Subject: decoder/sndfile: new decoder plugin based on libsndfile --- INSTALL | 3 + Makefile.am | 6 + NEWS | 1 + configure.ac | 32 +++++ src/decoder/sndfile_decoder_plugin.c | 246 +++++++++++++++++++++++++++++++++++ src/decoder_list.c | 4 + 6 files changed, 292 insertions(+) create mode 100644 src/decoder/sndfile_decoder_plugin.c diff --git a/INSTALL b/INSTALL index 3a1671a82..22a06fa62 100644 --- a/INSTALL +++ b/INSTALL @@ -104,6 +104,9 @@ For MIDI support (DO NOT USE - use libwildmidi instead) libwildmidi - http://wildmidi.sourceforge.net/ For MIDI support. +libsndfile - http://www.mega-nerd.com/libsndfile/ +WAVE, AIFF, and many others. + Optional Miscellaneous Dependencies ----------------------------------- diff --git a/Makefile.am b/Makefile.am index 19658d77c..37d5b3a62 100644 --- a/Makefile.am +++ b/Makefile.am @@ -325,6 +325,7 @@ endif DECODER_CFLAGS = \ $(VORBIS_CFLAGS) $(TREMOR_CFLAGS) \ $(patsubst -I%/FLAC,-I%,$(FLAC_CFLAGS)) \ + $(SNDFILE_CFLAGS) \ $(AUDIOFILE_CFLAGS) \ $(LIBMIKMOD_CFLAGS) \ $(MODPLUG_CFLAGS) \ @@ -338,6 +339,7 @@ DECODER_CFLAGS = \ DECODER_LIBS = \ $(VORBIS_LIBS) $(TREMOR_LIBS) \ $(FLAC_LIBS) \ + $(SNDFILE_LIBS) \ $(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \ $(MODPLUG_LIBS) \ $(SIDPLAY_LIBS) \ @@ -420,6 +422,10 @@ if HAVE_FFMPEG DECODER_SRC += src/decoder/ffmpeg_plugin.c endif +if ENABLE_SNDFILE +DECODER_SRC += src/decoder/sndfile_decoder_plugin.c +endif + # encoder plugins ENCODER_CFLAGS = \ diff --git a/NEWS b/NEWS index 047fa10b9..cea099966 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ ver 0.16 (20??/??/??) - id3: revised "performer" tag support * decoders: - ffmpeg: support multiple tags + - sndfile: new decoder plugin based on libsndfile * mixers: - removed support for legacy mixer configuration - reimplemented software volume as mixer+filter plugin diff --git a/configure.ac b/configure.ac index c082bf1cd..667f53b5d 100644 --- a/configure.ac +++ b/configure.ac @@ -470,6 +470,27 @@ AC_ARG_ENABLE(vorbis, [disable Ogg Vorbis support (default: enable)]),, enable_vorbis=yes) +AC_ARG_ENABLE(sndfile, + AS_HELP_STRING([--enable-sndfile], + [enable sndfile support]),, + enable_sndfile=auto) + +if test x$enable_sndfile = xauto && test x$enable_modplug = xyes; then + dnl If modplug is enabled, enable sndfile only if explicitly + dnl requested - modplug's modplug/sndfile.h is known to + dnl conflict with libsndfile's sndfile.h. + AC_MSG_NOTICE([disabling libsndfile auto-detection, because the modplug decoder is enabled]) + enable_sndfile=no +fi + +MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile], + [libsndfile decoder plugin], [libsndfile not found]) +AM_CONDITIONAL(ENABLE_SNDFILE, test x$enable_sndfile = xyes) +if test x$enable_sndfile = xyes; then + AC_DEFINE(ENABLE_SNDFILE, 1, [Define to enable the sndfile decoder plugin]) +fi + + dnl ### dnl Ogg Tremor dnl ### @@ -1380,6 +1401,12 @@ else echo " Ogg Vorbis support ............disabled" fi +if test x$enable_sndfile = xyes; then + echo " libsndfile ....................enabled" +else + echo " libsndfile ....................disabled" +fi + if test x$enable_audiofile = xyes; then echo " Wave file support .............enabled" else @@ -1492,6 +1519,11 @@ echo "" echo "##########################################" echo "" +if test x$enable_sndfile = xyes && test x$enable_modplug = xyes; then + AC_MSG_WARN([compilation may fail, because libmodplug conflicts with libsndfile]) + AC_MSG_WARN([libmodplug ships modplug/sndfile.h, which hides libsndfile's sndfile.h]) +fi + echo "Generating needed files for compilation" echo "" diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c new file mode 100644 index 000000000..0c5d2f063 --- /dev/null +++ b/src/decoder/sndfile_decoder_plugin.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2003-2009 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "decoder_api.h" + +#include + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "sndfile" + +static sf_count_t +sndfile_vio_get_filelen(void *user_data) +{ + const struct input_stream *is = user_data; + + return is->size; +} + +static sf_count_t +sndfile_vio_seek(sf_count_t offset, int whence, void *user_data) +{ + struct input_stream *is = user_data; + bool success; + + success = input_stream_seek(is, offset, whence); + if (!success) + return -1; + + return is->offset; +} + +static sf_count_t +sndfile_vio_read(void *ptr, sf_count_t count, void *user_data) +{ + struct input_stream *is = user_data; + size_t nbytes; + + nbytes = input_stream_read(is, ptr, count); + if (nbytes == 0 && is->error != 0) + return -1; + + return nbytes; +} + +static sf_count_t +sndfile_vio_write(G_GNUC_UNUSED const void *ptr, + G_GNUC_UNUSED sf_count_t count, + G_GNUC_UNUSED void *user_data) +{ + /* no writing! */ + return -1; +} + +static sf_count_t +sndfile_vio_tell(void *user_data) +{ + const struct input_stream *is = user_data; + + return is->offset; +} + +/** + * This SF_VIRTUAL_IO implementation wraps MPD's #input_stream to a + * libsndfile stream. + */ +static SF_VIRTUAL_IO vio = { + .get_filelen = sndfile_vio_get_filelen, + .seek = sndfile_vio_seek, + .read = sndfile_vio_read, + .write = sndfile_vio_write, + .tell = sndfile_vio_tell, +}; + +/** + * Converts a frame number to a timestamp (in seconds). + */ +static float +frame_to_time(sf_count_t frame, const struct audio_format *audio_format) +{ + return (float)frame / (float)audio_format->sample_rate; +} + +/** + * Converts a timestamp (in seconds) to a frame number. + */ +static sf_count_t +time_to_frame(float t, const struct audio_format *audio_format) +{ + return (sf_count_t)(t * audio_format->sample_rate); +} + +static void +sndfile_stream_decode(struct decoder *decoder, struct input_stream *is) +{ + SNDFILE *sf; + SF_INFO info; + struct audio_format audio_format; + size_t frame_size; + sf_count_t read_frames, num_frames, position = 0; + int buffer[4096]; + enum decoder_command cmd; + + info.format = 0; + + sf = sf_open_virtual(&vio, SFM_READ, &info, is); + if (sf == NULL) { + g_warning("sf_open_virtual() failed"); + return; + } + + audio_format.sample_rate = info.samplerate; + /* for now, always read 32 bit samples. Later, we could lower + MPD's CPU usage by reading 16 bit samples with + sf_readf_short() on low-quality source files. */ + audio_format.bits = 32; + audio_format.channels = info.channels; + + if (!audio_format_valid(&audio_format)) { + g_warning("invalid audio format"); + return; + } + + decoder_initialized(decoder, &audio_format, info.seekable, + frame_to_time(info.frames, &audio_format)); + + frame_size = audio_format_frame_size(&audio_format); + read_frames = sizeof(buffer) / frame_size; + + do { + num_frames = sf_readf_int(sf, buffer, read_frames); + if (num_frames <= 0) + break; + + cmd = decoder_data(decoder, is, + buffer, num_frames * frame_size, + frame_to_time(position, &audio_format), + 0, NULL); + if (cmd == DECODE_COMMAND_SEEK) { + sf_count_t c = + time_to_frame(decoder_seek_where(decoder), + &audio_format); + c = sf_seek(sf, c, SEEK_SET); + if (c < 0) + decoder_seek_error(decoder); + else + decoder_command_finished(decoder); + cmd = DECODE_COMMAND_NONE; + } + } while (cmd == DECODE_COMMAND_NONE); + + sf_close(sf); +} + +static struct tag * +sndfile_tag_dup(const char *path_fs) +{ + SNDFILE *sf; + SF_INFO info; + struct tag *tag; + const char *p; + + info.format = 0; + + sf = sf_open(path_fs, SFM_READ, &info); + if (sf == NULL) + return NULL; + + if (!audio_valid_sample_rate(info.samplerate)) { + sf_close(sf); + g_warning("Invalid sample rate in %s\n", path_fs); + return NULL; + } + + tag = tag_new(); + tag->time = info.frames / info.samplerate; + + p = sf_get_string(sf, SF_STR_TITLE); + if (p != NULL) + tag_add_item(tag, TAG_ITEM_TITLE, p); + + p = sf_get_string(sf, SF_STR_ARTIST); + if (p != NULL) + tag_add_item(tag, TAG_ITEM_ARTIST, p); + + p = sf_get_string(sf, SF_STR_DATE); + if (p != NULL) + tag_add_item(tag, TAG_ITEM_DATE, p); + + sf_close(sf); + + return tag; +} + +static const char *const sndfile_suffixes[] = { + "wav", "aiff", "aif", /* Microsoft / SGI / Apple */ + "au", "snd", /* Sun / DEC / NeXT */ + "paf", /* Paris Audio File */ + "iff", "svx", /* Commodore Amiga IFF / SVX */ + "sf", /* IRCAM */ + "voc", /* Creative */ + "w64", /* Soundforge */ + "pvf", /* Portable Voice Format */ + "xi", /* Fasttracker */ + "htk", /* HMM Tool Kit */ + "caf", /* Apple */ + "sd2", /* Sound Designer II */ + + /* libsndfile also supports FLAC and Ogg Vorbis, but only by + linking with libFLAC and libvorbis - we can do better, we + have native plugins for these libraries */ + + NULL +}; + +static const char *const sndfile_mime_types[] = { + "audio/x-wav", + "audio/x-aiff", + + /* what are the MIME types of the other supported formats? */ + + NULL +}; + +const struct decoder_plugin sndfile_decoder_plugin = { + .name = "sndfile", + .stream_decode = sndfile_stream_decode, + .tag_dup = sndfile_tag_dup, + .suffixes = sndfile_suffixes, + .mime_types = sndfile_mime_types, +}; diff --git a/src/decoder_list.c b/src/decoder_list.c index a42585e34..177ac46e4 100644 --- a/src/decoder_list.c +++ b/src/decoder_list.c @@ -31,6 +31,7 @@ extern const struct decoder_plugin mad_decoder_plugin; extern const struct decoder_plugin vorbis_decoder_plugin; extern const struct decoder_plugin flac_decoder_plugin; extern const struct decoder_plugin oggflac_decoder_plugin; +extern const struct decoder_plugin sndfile_decoder_plugin; extern const struct decoder_plugin audiofile_decoder_plugin; extern const struct decoder_plugin mp4ff_decoder_plugin; extern const struct decoder_plugin faad_decoder_plugin; @@ -56,6 +57,9 @@ static const struct decoder_plugin *const decoder_plugins[] = { #ifdef HAVE_FLAC &flac_decoder_plugin, #endif +#ifdef ENABLE_SNDFILE + &sndfile_decoder_plugin, +#endif #ifdef HAVE_AUDIOFILE &audiofile_decoder_plugin, #endif -- cgit v1.2.3