ref: 9b1f7f515d0173a16a263f2d16a0b3a541530177
parent: 586dec6fe8c26f1422b110f9e74b31e98bc9ef2e
author: robs <robs>
date: Sun Sep 27 11:43:13 EDT 2009
Refactor dynamic loading, fix MP3 issues - ID: 2859256 - latest patch
--- a/src/mp3-util.h
+++ b/src/mp3-util.h
@@ -30,7 +30,7 @@
{NULL, NULL}
};
-#if defined(HAVE_LAME_LAME_H) && defined(HAVE_ID3TAG_SET_FIELDVALUE)
+#if defined(HAVE_LAME)
static void write_comments(sox_format_t * ft)
{
@@ -41,6 +41,9 @@
const char* comment;
size_t required_size;
+ p->id3tag_init(p->gfp);
+ p->id3tag_set_pad(p->gfp, ID3PADDING);
+
for (i = 0; id3tagmap[i][0]; i++)
{
comment = sox_find_comment(ft->oob.comments, id3tagmap[i][1]);
@@ -69,7 +72,7 @@
free(id3tag_buf);
}
-#endif /* HAVE_LAME_LAME_H && HAVE_ID3TAG_SET_FIELDVALUE */
+#endif /* HAVE_LAME */
#ifdef USING_ID3TAG
@@ -133,11 +136,11 @@
static void mad_timer_mult(mad_timer_t * t, double d)
{
- t->seconds = d *= (t->seconds + t->fraction * (1. / MAD_TIMER_RESOLUTION));
- t->fraction = (d - t->seconds) * MAD_TIMER_RESOLUTION + .5;
+ t->seconds = (signed long)(d *= (t->seconds + t->fraction * (1. / MAD_TIMER_RESOLUTION)));
+ t->fraction = (unsigned long)((d - t->seconds) * MAD_TIMER_RESOLUTION + .5);
}
-static size_t mp3_duration_ms(sox_format_t * ft, unsigned char *buffer)
+static size_t mp3_duration_ms(sox_format_t * ft)
{
priv_t * p = (priv_t *) ft->priv;
FILE * fp = ft->fp;
@@ -157,15 +160,15 @@
int read, padding = 0;
size_t leftover = mad_stream.bufend - mad_stream.next_frame;
- memcpy(buffer, mad_stream.this_frame, leftover);
- read = fread(buffer + leftover, (size_t) 1, INPUT_BUFFER_SIZE - leftover, fp);
+ memcpy(p->mp3_buffer, mad_stream.this_frame, leftover);
+ read = fread(p->mp3_buffer + leftover, (size_t) 1, p->mp3_buffer_size - leftover, fp);
if (read <= 0) {
lsx_debug("got exact duration by scan to EOF (frames=%lu leftover=%lu)", (unsigned long)frames, (unsigned long)leftover);
break;
}
- for (; !depadded && padding < read && !buffer[padding]; ++padding);
+ for (; !depadded && padding < read && !p->mp3_buffer[padding]; ++padding);
depadded = sox_true;
- p->mad_stream_buffer(&mad_stream, buffer + padding, leftover + read - padding);
+ p->mad_stream_buffer(&mad_stream, p->mp3_buffer + padding, leftover + read - padding);
while (sox_true) { /* Decode frame headers */
mad_stream.error = MAD_ERROR_NONE;
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -10,17 +10,24 @@
*/
#include "sox_i.h"
+#include <string.h>
-#if defined(HAVE_LAME_LAME_H) || defined (HAVE_MAD_H)
+#if defined(HAVE_LAME_LAME_H) || defined(HAVE_LAME_H)
+#define HAVE_LAME 1
+#else
+#undef HAVE_LAME
+#endif
-#include <string.h>
+#if defined(HAVE_MAD_H) || defined(HAVE_LAME)
#ifdef HAVE_MAD_H
#include <mad.h>
#endif
-#ifdef HAVE_LAME_LAME_H
+#if defined(HAVE_LAME_LAME_H)
#include <lame/lame.h>
+#elif defined(HAVE_LAME_H)
+#include <lame.h>
#endif
#if defined(HAVE_ID3TAG) && (defined(HAVE_IO_H) || defined(HAVE_UNISTD_H))
@@ -38,89 +45,139 @@
#define ID3_TAG_FLAG_FOOTERPRESENT 0x10
#endif
-#if defined HAVE_LIBLTDL
- #include <ltdl.h>
-#if defined HAVE_MAD_H && defined DL_MAD
-mad_timer_t const mad_timer_zero;
+#ifndef HAVE_LIBLTDL
+ #undef DL_LAME
+ #undef DL_MAD
#endif
+
+/* Under Windows, importing data from DLLs is a dicey proposition. This is true
+ * when using dlopen, but also true if linking directly against the DLL if the
+ * header does not mark the data as __declspec(dllexport), which mad.h does not.
+ * Sidestep the issue by defining our own mad_timer_zero. This is needed because
+ * mad_timer_zero is used in some of the mad.h macros.
+ */
+#define mad_timer_zero mad_timer_zero_stub
+static mad_timer_t const mad_timer_zero_stub = {0, 0};
+
+#define MAXFRAMESIZE 2880
+#define ID3PADDING 128
+
+static const char* const mad_library_names[] =
+{
+#ifdef DL_MAD
+ "libmad",
+ "cygmad-0",
#endif
+ NULL
+};
-#define INPUT_BUFFER_SIZE (sox_globals.bufsiz)
+#ifdef DL_MAD
+ #define MAD_FUNC LSX_DLENTRY_DYNAMIC
+#else
+ #define MAD_FUNC LSX_DLENTRY_STATIC
+#endif
+#define MAD_FUNC_ENTRIES(f,x) \
+ MAD_FUNC(f,x, void, mad_stream_buffer, (struct mad_stream *, unsigned char const *, unsigned long)) \
+ MAD_FUNC(f,x, void, mad_stream_skip, (struct mad_stream *, unsigned long)) \
+ MAD_FUNC(f,x, int, mad_stream_sync, (struct mad_stream *)) \
+ MAD_FUNC(f,x, void, mad_stream_init, (struct mad_stream *)) \
+ MAD_FUNC(f,x, void, mad_frame_init, (struct mad_frame *)) \
+ MAD_FUNC(f,x, void, mad_synth_init, (struct mad_synth *)) \
+ MAD_FUNC(f,x, int, mad_frame_decode, (struct mad_frame *, struct mad_stream *)) \
+ MAD_FUNC(f,x, void, mad_timer_add, (mad_timer_t *, mad_timer_t)) \
+ MAD_FUNC(f,x, void, mad_synth_frame, (struct mad_synth *, struct mad_frame const *)) \
+ MAD_FUNC(f,x, char const *, mad_stream_errorstr, (struct mad_stream const *)) \
+ MAD_FUNC(f,x, void, mad_frame_finish, (struct mad_frame *)) \
+ MAD_FUNC(f,x, void, mad_stream_finish, (struct mad_stream *)) \
+ MAD_FUNC(f,x, unsigned long, mad_bit_read, (struct mad_bitptr *, unsigned int)) \
+ MAD_FUNC(f,x, int, mad_header_decode, (struct mad_header *, struct mad_stream *)) \
+ MAD_FUNC(f,x, void, mad_header_init, (struct mad_header *)) \
+ MAD_FUNC(f,x, signed long, mad_timer_count, (mad_timer_t, enum mad_units)) \
+ MAD_FUNC(f,x, void, mad_timer_multiply, (mad_timer_t *, signed long))
+
+static const char* const lame_library_names[] =
+{
+#ifdef DL_LAME
+ "libmp3lame",
+ "lame-enc",
+#endif
+ NULL
+};
+
+#ifdef DL_LAME
+ #define LAME_FUNC LSX_DLENTRY_DYNAMIC
+ #define LAME_FUNC_MSG LSX_DLENTRY_STUB
+ #define LAME_FUNC_LAMETAG LSX_DLENTRY_STUB
+ #define LAME_FUNC_ID3 LSX_DLENTRY_STUB
+#else
+ #define LAME_FUNC LSX_DLENTRY_STATIC
+ #ifdef HAVE_LAME_SET_MSGF
+ #define LAME_FUNC_MSG LSX_DLENTRY_STATIC
+ #else
+ #define LAME_FUNC_MSG LSX_DLENTRY_STUB
+ #endif
+ #ifdef HAVE_LAME_GET_LAMETAG_FRAME
+ #define LAME_FUNC_LAMETAG LSX_DLENTRY_STATIC
+ #else
+ #define LAME_FUNC_LAMETAG LSX_DLENTRY_STUB
+ #endif
+ #ifdef HAVE_LAME_ID3TAG
+ #define LAME_FUNC_ID3 LSX_DLENTRY_STATIC
+ #else
+ #define LAME_FUNC_ID3 LSX_DLENTRY_STUB
+ #endif
+#endif
+
+#define LAME_FUNC_ENTRIES(f,x) \
+ LAME_FUNC(f,x, lame_global_flags*, lame_init, (void)) \
+ LAME_FUNC_MSG(f,x, int, lame_set_errorf, (lame_global_flags *, void (*)(const char *, va_list))) \
+ LAME_FUNC_MSG(f,x, int, lame_set_debugf, (lame_global_flags *, void (*)(const char *, va_list))) \
+ LAME_FUNC_MSG(f,x, int, lame_set_msgf, (lame_global_flags *, void (*)(const char *, va_list))) \
+ LAME_FUNC(f,x, int, lame_set_num_samples, (lame_global_flags *, unsigned long)) \
+ LAME_FUNC(f,x, int, lame_get_num_channels, (const lame_global_flags *)) \
+ LAME_FUNC(f,x, int, lame_set_num_channels, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_set_in_samplerate, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_set_out_samplerate, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_set_bWriteVbrTag, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_set_brate, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_set_quality, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, vbr_mode, lame_get_VBR, (const lame_global_flags *)) \
+ LAME_FUNC(f,x, int, lame_set_VBR, (lame_global_flags *, vbr_mode)) \
+ LAME_FUNC(f,x, int, lame_set_VBR_q, (lame_global_flags *, int)) \
+ LAME_FUNC(f,x, int, lame_init_params, (lame_global_flags *)) \
+ LAME_FUNC(f,x, int, lame_encode_buffer, (lame_global_flags *, const short int[], const short int[], const int, unsigned char *, const int)) \
+ LAME_FUNC(f,x, int, lame_encode_flush, (lame_global_flags *, unsigned char *, int)) \
+ LAME_FUNC(f,x, int, lame_close, (lame_global_flags *)) \
+ LAME_FUNC_LAMETAG(f,x, size_t, lame_get_lametag_frame, (const lame_global_flags *, unsigned char*, size_t)) \
+ LAME_FUNC_ID3(f,x, size_t, lame_get_id3v2_tag, (lame_global_flags *, unsigned char*, size_t)) \
+ LAME_FUNC_ID3(f,x, void, id3tag_init, (lame_global_flags *)) \
+ LAME_FUNC_ID3(f,x, void, id3tag_set_pad, (lame_global_flags *, size_t)) \
+ LAME_FUNC_ID3(f,x, int, id3tag_set_fieldvalue, (lame_global_flags *, const char *))
+
/* Private data */
-typedef struct {
+typedef struct mp3_priv_t {
+ unsigned char *mp3_buffer;
+ size_t mp3_buffer_size;
+
#ifdef HAVE_MAD_H
struct mad_stream Stream;
struct mad_frame Frame;
struct mad_synth Synth;
mad_timer_t Timer;
- unsigned char *InputBuffer;
ptrdiff_t cursamp;
size_t FrameCount;
-
- void (*mad_stream_buffer)(struct mad_stream *, unsigned char const *,
- unsigned long);
- void (*mad_stream_skip)(struct mad_stream *, unsigned long);
- int (*mad_stream_sync)(struct mad_stream *);
- void (*mad_stream_init)(struct mad_stream *);
- void (*mad_frame_init)(struct mad_frame *);
- void (*mad_synth_init)(struct mad_synth *);
- int (*mad_frame_decode)(struct mad_frame *, struct mad_stream *);
- void (*mad_timer_add)(mad_timer_t *, mad_timer_t);
- void (*mad_synth_frame)(struct mad_synth *, struct mad_frame const *);
- char const *(*mad_stream_errorstr)(struct mad_stream const *);
- void (*mad_frame_finish)(struct mad_frame *);
- void (*mad_stream_finish)(struct mad_stream *);
- unsigned long (*mad_bit_read)(struct mad_bitptr *, unsigned int);
- int (*mad_header_decode)(struct mad_header *, struct mad_stream *);
- void (*mad_header_init)(struct mad_header *);
- signed long (*mad_timer_count)(mad_timer_t, enum mad_units);
- void (*mad_timer_multiply)(mad_timer_t *, signed long);
- #if defined HAVE_LIBLTDL && defined DL_MAD
- lt_dlhandle mad_lth;
- #endif
+ LSX_DLENTRIES_TO_PTRS(MAD_FUNC_ENTRIES, mad_dl);
#endif /*HAVE_MAD_H*/
-#ifdef HAVE_LAME_LAME_H
- lame_global_flags *gfp;
-
- lame_global_flags * (*lame_init)(void);
- int (*lame_set_num_channels)(lame_global_flags *, int);
- int (*lame_get_num_channels)(const lame_global_flags *);
- int (*lame_set_in_samplerate)(lame_global_flags *, int);
- int (*lame_set_bWriteVbrTag)(lame_global_flags *, int);
- int (*lame_get_bWriteVbrTag)(lame_global_flags const *);
- #ifdef HAVE_ID3TAG_SET_FIELDVALUE
- int (*id3tag_set_fieldvalue)(lame_global_flags *, const char *);
- #endif
- int (*lame_init_params)(lame_global_flags *);
- int (*lame_set_errorf)(lame_global_flags *,
- void (*func)(const char *, va_list));
- int (*lame_set_debugf)(lame_global_flags *,
- void (*func)(const char *, va_list));
- int (*lame_set_msgf)(lame_global_flags *,
- void (*func)(const char *, va_list));
- int (*lame_encode_buffer)(lame_global_flags *, const short int[],
- const short int[], const int,
- unsigned char *, const int);
- int (*lame_encode_flush)(lame_global_flags *, unsigned char *,
- int);
- int (*lame_close)(lame_global_flags *);
- void (*lame_mp3_tags_fid)(lame_global_flags *, FILE *);
- int (*lame_get_brate)(const lame_global_flags *);
- int (*lame_set_brate)(lame_global_flags *, int);
- int (*lame_set_quality)(lame_global_flags *, int);
- int (*lame_set_VBR)(lame_global_flags *, vbr_mode);
- int (*lame_set_VBR_min_bitrate_kbps)(lame_global_flags *, int);
- #ifdef HAVE_LAME_SET_VBR_QUALITY
- int (*lame_set_VBR_quality)(lame_global_flags *, float);
- #endif
- vbr_mode (*lame_get_VBR)(const lame_global_flags *);
-
- #if defined HAVE_LIBLTDL && defined DL_LAME
- lt_dlhandle lame_lth;
- #endif
-#endif /*HAVE_LAME_LAME_H*/
+#ifdef HAVE_LAME
+ lame_global_flags *gfp;
+ short *pcm_buffer;
+ size_t pcm_buffer_size;
+ unsigned long num_samples;
+ int vbr_tag;
+ LSX_DLENTRIES_TO_PTRS(LAME_FUNC_ENTRIES, lame_dl);
+#endif /*HAVE_LAME*/
} priv_t;
/* This function merges the functions tagtype() and id3_tag_query()
@@ -179,16 +236,16 @@
* TODO: Is 2016 bytes the size of the largest frame?
* (448000*(1152/32000))/8
*/
- memmove(p->InputBuffer, p->Stream.next_frame, remaining);
+ memmove(p->mp3_buffer, p->Stream.next_frame, remaining);
- bytes_read = lsx_readbuf(ft, p->InputBuffer+remaining,
- INPUT_BUFFER_SIZE-remaining);
+ bytes_read = lsx_readbuf(ft, p->mp3_buffer+remaining,
+ p->mp3_buffer_size-remaining);
if (bytes_read == 0)
{
return SOX_EOF;
}
- p->mad_stream_buffer(&p->Stream, p->InputBuffer, bytes_read+remaining);
+ p->mad_stream_buffer(&p->Stream, p->mp3_buffer, bytes_read+remaining);
p->Stream.error = 0;
return SOX_SUCCESS;
@@ -238,141 +295,110 @@
priv_t *p = (priv_t *) ft->priv;
size_t ReadSize;
sox_bool ignore_length = ft->signal.length == SOX_IGNORE_LENGTH;
+ int open_library_result;
-#if defined HAVE_LIBLTDL && defined DL_MAD
- #define DL_LIB_NAME "MAD decoder library (libmad"
- #define LOAD_FN_PTR(x) \
- if (!(ltptr.ptr = lt_dlsym(p->mad_lth, #x))) { \
- lsx_fail("incompatible " DL_LIB_NAME " is missing "#x")"); \
- return SOX_EOF; \
- } \
- p->x = ltptr.fn;
- union {void (* fn)(); lt_ptr ptr;} ltptr;
- if (!lt_dlinit())
- p->mad_lth = lt_dlopenext("libmad");
- if (!p->mad_lth) {
- lsx_fail("could not find " DL_LIB_NAME ")");
+ LSX_DLLIBRARY_OPEN(
+ p,
+ mad_dl,
+ MAD_FUNC_ENTRIES,
+ "MAD decoder library",
+ mad_library_names,
+ open_library_result);
+ if (open_library_result)
return SOX_EOF;
- }
-#else
- #define DL_LIB_NAME
- #define LOAD_FN_PTR(x) p->x = x;
-#endif
- LOAD_FN_PTR(mad_bit_read)
- LOAD_FN_PTR(mad_frame_decode)
- LOAD_FN_PTR(mad_frame_finish)
- LOAD_FN_PTR(mad_frame_init)
- LOAD_FN_PTR(mad_header_decode)
- LOAD_FN_PTR(mad_header_init)
- LOAD_FN_PTR(mad_stream_buffer)
- LOAD_FN_PTR(mad_stream_errorstr)
- LOAD_FN_PTR(mad_stream_finish)
- LOAD_FN_PTR(mad_stream_init)
- LOAD_FN_PTR(mad_stream_skip)
- LOAD_FN_PTR(mad_stream_sync)
- LOAD_FN_PTR(mad_synth_frame)
- LOAD_FN_PTR(mad_synth_init)
- LOAD_FN_PTR(mad_timer_add)
- LOAD_FN_PTR(mad_timer_count)
- LOAD_FN_PTR(mad_timer_multiply)
+ p->mp3_buffer_size = sox_globals.bufsiz;
+ p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);
-#undef LOAD_FN_PTR
-#undef DL_LIB_NAME
-
- p->InputBuffer = NULL;
-
- p->InputBuffer=lsx_malloc(INPUT_BUFFER_SIZE);
-
- ft->signal.length = SOX_UNSPEC;
- if (ft->seekable) {
+ ft->signal.length = SOX_UNSPEC;
+ if (ft->seekable) {
#ifdef USING_ID3TAG
- read_comments(ft);
- rewind(ft->fp);
- if (!ft->signal.length)
+ read_comments(ft);
+ rewind(ft->fp);
+ if (!ft->signal.length)
#endif
- if (!ignore_length)
- ft->signal.length = mp3_duration_ms(ft, p->InputBuffer);
- }
+ if (!ignore_length)
+ ft->signal.length = mp3_duration_ms(ft);
+ }
- p->mad_stream_init(&p->Stream);
- p->mad_frame_init(&p->Frame);
- p->mad_synth_init(&p->Synth);
- mad_timer_reset(&p->Timer);
+ p->mad_stream_init(&p->Stream);
+ p->mad_frame_init(&p->Frame);
+ p->mad_synth_init(&p->Synth);
+ mad_timer_reset(&p->Timer);
- ft->encoding.encoding = SOX_ENCODING_MP3;
+ ft->encoding.encoding = SOX_ENCODING_MP3;
- /* Decode at least one valid frame to find out the input
- * format. The decoded frame will be saved off so that it
- * can be processed later.
- */
- ReadSize = lsx_readbuf(ft, p->InputBuffer, INPUT_BUFFER_SIZE);
- if (ReadSize != INPUT_BUFFER_SIZE && ferror(ft->fp))
- return SOX_EOF;
+ /* Decode at least one valid frame to find out the input
+ * format. The decoded frame will be saved off so that it
+ * can be processed later.
+ */
+ ReadSize = lsx_readbuf(ft, p->mp3_buffer, p->mp3_buffer_size);
+ if (ReadSize != p->mp3_buffer_size && ferror(ft->fp))
+ return SOX_EOF;
- p->mad_stream_buffer(&p->Stream, p->InputBuffer, ReadSize);
+ p->mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize);
- /* Find a valid frame before starting up. This makes sure
- * that we have a valid MP3 and also skips past ID3v2 tags
- * at the beginning of the audio file.
- */
- p->Stream.error = 0;
- while (p->mad_frame_decode(&p->Frame,&p->Stream))
- {
- /* check whether input buffer needs a refill */
- if (p->Stream.error == MAD_ERROR_BUFLEN)
- {
- if (sox_mp3_input(ft) == SOX_EOF)
- return SOX_EOF;
+ /* Find a valid frame before starting up. This makes sure
+ * that we have a valid MP3 and also skips past ID3v2 tags
+ * at the beginning of the audio file.
+ */
+ p->Stream.error = 0;
+ while (p->mad_frame_decode(&p->Frame,&p->Stream))
+ {
+ /* check whether input buffer needs a refill */
+ if (p->Stream.error == MAD_ERROR_BUFLEN)
+ {
+ if (sox_mp3_input(ft) == SOX_EOF)
+ return SOX_EOF;
- continue;
- }
+ continue;
+ }
- /* Consume any ID3 tags */
- sox_mp3_inputtag(ft);
+ /* Consume any ID3 tags */
+ sox_mp3_inputtag(ft);
- /* FIXME: We should probably detect when we've read
- * a bunch of non-ID3 data and still haven't found a
- * frame. In that case we can abort early without
- * scanning the whole file.
- */
- p->Stream.error = 0;
- }
+ /* FIXME: We should probably detect when we've read
+ * a bunch of non-ID3 data and still haven't found a
+ * frame. In that case we can abort early without
+ * scanning the whole file.
+ */
+ p->Stream.error = 0;
+ }
- if (p->Stream.error)
- {
- lsx_fail_errno(ft,SOX_EOF,"No valid MP3 frame found");
- return SOX_EOF;
- }
-
- switch(p->Frame.header.mode)
- {
- case MAD_MODE_SINGLE_CHANNEL:
- case MAD_MODE_DUAL_CHANNEL:
- case MAD_MODE_JOINT_STEREO:
- case MAD_MODE_STEREO:
- ft->signal.channels = MAD_NCHANNELS(&p->Frame.header);
- break;
- default:
- lsx_fail_errno(ft, SOX_EFMT, "Cannot determine number of channels");
- return SOX_EOF;
- }
+ if (p->Stream.error)
+ {
+ lsx_fail_errno(ft,SOX_EOF,"No valid MP3 frame found");
+ return SOX_EOF;
+ }
- p->FrameCount=1;
+ switch(p->Frame.header.mode)
+ {
+ case MAD_MODE_SINGLE_CHANNEL:
+ case MAD_MODE_DUAL_CHANNEL:
+ case MAD_MODE_JOINT_STEREO:
+ case MAD_MODE_STEREO:
+ ft->signal.channels = MAD_NCHANNELS(&p->Frame.header);
+ break;
+ default:
+ lsx_fail_errno(ft, SOX_EFMT, "Cannot determine number of channels");
+ return SOX_EOF;
+ }
- p->mad_timer_add(&p->Timer,p->Frame.header.duration);
- p->mad_synth_frame(&p->Synth,&p->Frame);
- ft->signal.rate=p->Synth.pcm.samplerate;
- if (ignore_length)
- ft->signal.length = SOX_UNSPEC;
- else {
- ft->signal.length = ft->signal.length * .001 * ft->signal.rate + .5;
- ft->signal.length *= ft->signal.channels; /* Keep separate from line above! */
- }
+ p->FrameCount=1;
- p->cursamp = 0;
+ p->mad_timer_add(&p->Timer,p->Frame.header.duration);
+ p->mad_synth_frame(&p->Synth,&p->Frame);
+ ft->signal.rate=p->Synth.pcm.samplerate;
+ if (ignore_length)
+ ft->signal.length = SOX_UNSPEC;
+ else {
+ ft->signal.length = (size_t)(ft->signal.length * .001 * ft->signal.rate + .5);
+ ft->signal.length *= ft->signal.channels; /* Keep separate from line above! */
+ }
- return SOX_SUCCESS;
+ p->cursamp = 0;
+
+ return SOX_SUCCESS;
}
/*
@@ -455,11 +481,8 @@
p->mad_frame_finish(&p->Frame);
p->mad_stream_finish(&p->Stream);
- free(p->InputBuffer);
-#if defined HAVE_LIBLTDL && defined DL_MAD
- if (!lt_dlclose(p->mad_lth))
- lt_dlexit();
-#endif
+ free(p->mp3_buffer);
+ LSX_DLLIBRARY_CLOSE(p, mad_dl);
return SOX_SUCCESS;
}
#else /*HAVE_MAD_H*/
@@ -472,7 +495,7 @@
#define stopread NULL
#endif /*HAVE_MAD_H*/
-#ifdef HAVE_LAME_LAME_H
+#ifdef HAVE_LAME
/* Adapters for lame message callbacks: */
@@ -500,60 +523,171 @@
return;
}
-static int startwrite(sox_format_t * ft)
+/* These functions are considered optional. If they aren't present in the
+ library, the stub versions defined here will be used instead. */
+
+static int lame_set_errorf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+ { return 0; }
+static int lame_set_debugf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+ { return 0; }
+static int lame_set_msgf_stub(lame_global_flags * gfp UNUSED, void (*func)(const char *, va_list) UNUSED)
+ { return 0; }
+static size_t lame_get_lametag_frame_stub(const lame_global_flags * gfp UNUSED, unsigned char * buffer UNUSED, size_t size UNUSED)
+ { return 0; }
+static size_t lame_get_id3v2_tag_stub(lame_global_flags * gfp UNUSED, unsigned char * buffer UNUSED, size_t size UNUSED)
+ { return 0; }
+static void id3tag_init_stub(lame_global_flags * gfp UNUSED)
+ { return; }
+static void id3tag_set_pad_stub(lame_global_flags * gfp UNUSED, size_t padding UNUSED)
+ { return; }
+static int id3tag_set_fieldvalue_stub(lame_global_flags * gfp UNUSED, const char *fieldvalue UNUSED)
+ { return 0; }
+
+static int get_id3v2_tag_size(sox_format_t * ft)
{
- priv_t *p = (priv_t *) ft->priv;
+ priv_t *p = (priv_t *)ft->priv;
+ FILE *fp = ft->fp;
+ size_t bytes_read;
+ int id3v2_size;
+ unsigned char id3v2_header[10];
-#if defined HAVE_LIBLTDL && defined DL_LAME
- #define DL_LIB_NAME "LAME encoder library (libmp3lame"
- #define LOAD_FN_PTR(x) \
- if (!(ltptr.ptr = lt_dlsym(p->lame_lth, #x))) { \
- lsx_fail("incompatible " DL_LIB_NAME " is missing "#x")"); \
- return SOX_EOF; \
- } \
- p->x = ltptr.fn;
- union {int (* fn)(); lt_ptr ptr;} ltptr;
- if (!lt_dlinit())
- p->lame_lth = lt_dlopenext("libmp3lame");
- if (!p->lame_lth) {
- lsx_fail("could not find " DL_LIB_NAME ")");
+ if (fseeko(fp, 0, SEEK_SET) != 0) {
+ lsx_warn("cannot update id3 tag - failed to seek to beginning");
return SOX_EOF;
}
-#else
- #define DL_LIB_NAME
- #define LOAD_FN_PTR(x) p->x = x;
-#endif
- LOAD_FN_PTR(lame_init)
- LOAD_FN_PTR(lame_set_num_channels)
- LOAD_FN_PTR(lame_get_num_channels)
- LOAD_FN_PTR(lame_set_in_samplerate)
- LOAD_FN_PTR(lame_set_bWriteVbrTag)
- LOAD_FN_PTR(lame_get_bWriteVbrTag)
-#ifdef HAVE_ID3TAG_SET_FIELDVALUE
- LOAD_FN_PTR(id3tag_set_fieldvalue)
-#endif
- LOAD_FN_PTR(lame_init_params)
- LOAD_FN_PTR(lame_set_errorf)
- LOAD_FN_PTR(lame_set_debugf)
- LOAD_FN_PTR(lame_set_msgf)
- LOAD_FN_PTR(lame_encode_buffer)
- LOAD_FN_PTR(lame_encode_flush)
- LOAD_FN_PTR(lame_close)
- LOAD_FN_PTR(lame_mp3_tags_fid)
- LOAD_FN_PTR(lame_get_brate)
- LOAD_FN_PTR(lame_set_brate)
- LOAD_FN_PTR(lame_set_quality)
- LOAD_FN_PTR(lame_set_VBR)
- LOAD_FN_PTR(lame_set_VBR_min_bitrate_kbps)
-#if HAVE_LAME_SET_VBR_QUALITY
- LOAD_FN_PTR(lame_set_VBR_quality)
-#endif
- LOAD_FN_PTR(lame_get_VBR)
+ /* read 10 bytes in case there's an ID3 version 2 header here */
+ bytes_read = fread(id3v2_header, 1, sizeof(id3v2_header), fp);
+ if (bytes_read != sizeof(id3v2_header)) {
+ lsx_warn("cannot update id3 tag - failed to read id3 header");
+ return SOX_EOF; /* not readable, maybe opened Write-Only */
+ }
-#undef LOAD_FN_PTR
-#undef DL_LIB_NAME
+ /* does the stream begin with the ID3 version 2 file identifier? */
+ if (!strncmp((char *) id3v2_header, "ID3", 3)) {
+ /* the tag size (minus the 10-byte header) is encoded into four
+ * bytes where the most significant bit is clear in each byte */
+ id3v2_size = (((id3v2_header[6] & 0x7f) << 21)
+ | ((id3v2_header[7] & 0x7f) << 14)
+ | ((id3v2_header[8] & 0x7f) << 7)
+ | (id3v2_header[9] & 0x7f))
+ + sizeof(id3v2_header);
+ } else {
+ /* no ID3 version 2 tag in this stream */
+ id3v2_size = 0;
+ }
+ return id3v2_size;
+}
+static void rewrite_id3v2_tag(sox_format_t * ft, int id3v2_size, unsigned long num_samples)
+{
+ priv_t *p = (priv_t *)ft->priv;
+ FILE *fp = ft->fp;
+ int new_size;
+ unsigned char *buffer = lsx_malloc(id3v2_size);
+
+ if (!buffer)
+ {
+ lsx_warn("cannot update id3 tag - failed to allocate buffer");
+ return;
+ }
+
+ p->lame_set_num_samples(p->gfp, num_samples);
+ lsx_debug("updated MP3 TLEN to %lu samples", num_samples);
+
+ p->id3tag_set_pad(p->gfp, ID3PADDING);
+ new_size = p->lame_get_id3v2_tag(p->gfp, buffer, id3v2_size);
+
+ if (new_size != id3v2_size && new_size-ID3PADDING <= id3v2_size) {
+ p->id3tag_set_pad(p->gfp, ID3PADDING + id3v2_size - new_size);
+ new_size = p->lame_get_id3v2_tag(p->gfp, buffer, id3v2_size);
+ }
+
+ if (new_size != id3v2_size) {
+ lsx_warn("cannot update id3 tag - new tag too big");
+ } else {
+ fseeko(fp, 0, SEEK_SET);
+ /* Overwrite the Id3v2 tag (this time TLEN should be accurate) */
+ if (fwrite(buffer, id3v2_size, 1, fp) != 1) {
+ lsx_debug("Rewrote Id3v2 tag (%d bytes)", id3v2_size);
+ }
+ }
+
+ free(buffer);
+}
+
+static void rewrite_tags(sox_format_t * ft, unsigned long num_samples)
+{
+ priv_t *p = (priv_t *)ft->priv;
+ FILE *fp = ft->fp;
+
+ off_t file_size;
+ int id3v2_size;
+
+ if (fseeko(fp, 0, SEEK_END)) {
+ lsx_warn("cannot update tags - seek to end failed");
+ return;
+ }
+
+ /* Get file size */
+ file_size = ftello(fp);
+
+ if (file_size == 0) {
+ lsx_warn("cannot update tags - file size is 0");
+ return;
+ }
+
+ id3v2_size = get_id3v2_tag_size(ft);
+ if (id3v2_size < 0) {
+ return;
+ } else if (id3v2_size > 0 && num_samples != p->num_samples) {
+ rewrite_id3v2_tag(ft, id3v2_size, num_samples);
+ }
+
+ if (p->vbr_tag) {
+ unsigned int lametag_size;
+ uint8_t buffer[MAXFRAMESIZE];
+
+ if (fseeko(fp, id3v2_size, SEEK_SET)) {
+ lsx_warn("cannot write VBR tag - seek to tag block failed");
+ return;
+ }
+
+ lametag_size = p->lame_get_lametag_frame(p->gfp, buffer, sizeof(buffer));
+ if (lametag_size > sizeof(buffer)) {
+ lsx_warn("cannot write VBR tag - VBR tag too large for buffer");
+ return;
+ }
+
+ if (lametag_size < 1) {
+ return;
+ }
+
+ if (fwrite(buffer, lametag_size, 1, fp) != 1) {
+ lsx_warn("cannot write VBR tag - VBR tag write failed");
+ } else {
+ lsx_debug("rewrote VBR tag (%u bytes)", lametag_size);
+ }
+ }
+}
+
+#define LAME_BUFFER_SIZE(num_samples) (((num_samples) + 3) / 4 * 5 + 7200)
+
+static int startwrite(sox_format_t * ft)
+{
+ priv_t *p = (priv_t *) ft->priv;
+ int openlibrary_result;
+
+ LSX_DLLIBRARY_OPEN(
+ p,
+ lame_dl,
+ LAME_FUNC_ENTRIES,
+ "LAME encoder library",
+ lame_library_names,
+ openlibrary_result);
+ if (openlibrary_result)
+ return SOX_EOF;
+
if (ft->encoding.encoding != SOX_ENCODING_MP3) {
if(ft->encoding.encoding != SOX_ENCODING_UNKNOWN)
lsx_report("Encoding forced to MP3");
@@ -560,6 +694,12 @@
ft->encoding.encoding = SOX_ENCODING_MP3;
}
+ p->mp3_buffer_size = LAME_BUFFER_SIZE(sox_globals.bufsiz);
+ p->mp3_buffer = lsx_malloc(p->mp3_buffer_size);
+
+ p->pcm_buffer_size = sox_globals.bufsiz * sizeof(short signed int);
+ p->pcm_buffer = lsx_malloc(p->pcm_buffer_size);
+
p->gfp = p->lame_init();
if (p->gfp == NULL){
@@ -572,6 +712,9 @@
p->lame_set_debugf(p->gfp,debugf);
p->lame_set_msgf (p->gfp,msgf);
+ p->num_samples = ft->signal.length == SOX_IGNORE_LENGTH ? 0 : ft->signal.length / max(ft->signal.channels, 1);
+ p->lame_set_num_samples(p->gfp, p->num_samples);
+
if (ft->signal.channels != SOX_ENCODING_UNKNOWN) {
if ( (p->lame_set_num_channels(p->gfp,(int)ft->signal.channels)) < 0) {
lsx_fail_errno(ft,SOX_EOF,"Unsupported number of channels");
@@ -582,13 +725,11 @@
ft->signal.channels = p->lame_get_num_channels(p->gfp); /* LAME default */
p->lame_set_in_samplerate(p->gfp,(int)ft->signal.rate);
+ p->lame_set_out_samplerate(p->gfp,(int)ft->signal.rate);
- p->lame_set_bWriteVbrTag(p->gfp, 0); /* disable writing VBR tag */
+ if (!LSX_DLFUNC_IS_STUB(p, id3tag_init))
+ write_comments(ft);
-#ifdef HAVE_ID3TAG_SET_FIELDVALUE
- write_comments(ft);
-#endif
-
/* The primary parameter to the LAME encoder is the bit rate. If the
* value of encoding.compression is a positive integer, it's taken as
* the bitrate in kbps (that is if you specify 128, it use 128 kbps).
@@ -602,9 +743,8 @@
* Because encoding.compression is a float, the fractional part is used
* to select quality. 128.2 selects 128 kbps encoding with a quality
* of 2. There is one problem with this approach. We need 128 to specify
- * 128 kbps encoding with default quality, so 0 means use default. Instead
- * of 0 you have to use .01 (or .99) to specify the highest quality
- * (128.01 or 128.99).
+ * 128 kbps encoding with default quality, so .0 means use default. Instead
+ * of .0 you have to use .01 to specify the highest quality (128.01).
*
* LAME uses bitrate to specify a constant bitrate, but higher quality
* can be achieved using Variable Bit Rate (VBR). VBR quality (really
@@ -634,64 +774,61 @@
double abs_compression = fabs(ft->encoding.compression);
double floor_compression = floor(abs_compression);
double fraction_compression = abs_compression - floor_compression;
+ int bitrate_q = (int)floor_compression;
+ int encoder_q =
+ fraction_compression == 0.0
+ ? -1
+ : (int)(fraction_compression * 10.0 + 0.5);
- if (floor(ft->encoding.compression) <= 0) {
+ if (ft->encoding.compression < 0.5) {
if (p->lame_get_VBR(p->gfp) == vbr_off)
p->lame_set_VBR(p->gfp, vbr_default);
if (ft->seekable) {
- p->lame_set_bWriteVbrTag(p->gfp, 1); /* enable writing VBR tag */
+ if (!LSX_DLFUNC_IS_STUB(p, lame_get_id3v2_tag) &&
+ !LSX_DLFUNC_IS_STUB(p, lame_get_lametag_frame)) {
+ p->vbr_tag = 1;
+ } else {
+ lsx_warn("unable to write VBR tag because lametag support is not present");
+ }
} else {
- lsx_warn("unable to write VBR Tag because we can't seek");
+ lsx_warn("unable to write VBR tag because we can't seek");
}
-#if HAVE_LAME_SET_VBR_QUALITY
-#define IGNORE_WARNING \
- if (p->lame_set_VBR_quality(p->gfp, floor_compression) < 0)
-#include "ignore-warning-1.h"
+ if (p->lame_set_VBR_q(p->gfp, bitrate_q) < 0)
{
lsx_fail_errno(ft, SOX_EOF,
- "lame_set_VBR_quality(%f) failed (should be between 0 and 9)",
- floor_compression);
+ "lame_set_VBR_q(%d) failed (should be between 0 and 9)",
+ bitrate_q);
return(SOX_EOF);
}
- lsx_report("lame_set_VBR_quality(%f)", floor_compression);
-#else
- /* TODO lsx_warn */
-#endif
+ lsx_report("lame_set_VBR_q(%d)", bitrate_q);
} else {
- if (p->lame_set_brate(p->gfp, (int)floor_compression) < 0) {
+ if (p->lame_set_brate(p->gfp, bitrate_q) < 0) {
lsx_fail_errno(ft, SOX_EOF,
- "lame_set_brate(%d) failed", (int)floor_compression);
+ "lame_set_brate(%d) failed", bitrate_q);
return(SOX_EOF);
}
- p->lame_set_VBR_min_bitrate_kbps(p->gfp, p->lame_get_brate(p->gfp));
- lsx_report("lame_set_brate(%d)", (int)floor_compression);
+ lsx_report("lame_set_brate(%d)", bitrate_q);
}
/* Set Quality */
- if (0.0 == fraction_compression) {
+ if (encoder_q < 0) {
/* use default quality value */
lsx_report("using MP3 default quality");
- }
- else if (fraction_compression <= 0.01 || 0.99 <= fraction_compression) {
- if (p->lame_set_quality(p->gfp, 0) < 0) {
- lsx_fail_errno(ft, SOX_EOF, "lame_set_quality(0) failed");
- return(SOX_EOF);
- }
- lsx_report("lame_set_quality(0)");
} else {
- int quality = (int)(0.5 + fraction_compression * 10);
- if (p->lame_set_quality(p->gfp, quality) < 0) {
+ if (p->lame_set_quality(p->gfp, encoder_q) < 0) {
lsx_fail_errno(ft, SOX_EOF,
- "lame_set_quality(%d) failed", quality);
+ "lame_set_quality(%d) failed", encoder_q);
return(SOX_EOF);
}
- lsx_report("lame_set_quality(%d)", quality);
+ lsx_report("lame_set_quality(%d)", encoder_q);
}
}
+ p->lame_set_bWriteVbrTag(p->gfp, p->vbr_tag);
+
if (p->lame_init_params(p->gfp) < 0){
lsx_fail_errno(ft,SOX_EOF,"LAME initialization failed");
return(SOX_EOF);
@@ -703,15 +840,24 @@
static size_t sox_mp3write(sox_format_t * ft, const sox_sample_t *buf, size_t samp)
{
priv_t *p = (priv_t *)ft->priv;
- unsigned char *mp3buffer;
- size_t mp3buffer_size;
+ size_t new_buffer_size;
short signed int *buffer_l, *buffer_r = NULL;
int nsamples = samp/ft->signal.channels;
int i,j;
- ptrdiff_t done = 0;
size_t written;
SOX_SAMPLE_LOCALS;
+ new_buffer_size = samp * sizeof(short signed int);
+ if (p->pcm_buffer_size < new_buffer_size) {
+ short *new_buffer = lsx_realloc(p->pcm_buffer, new_buffer_size);
+ if (!new_buffer) {
+ lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory");
+ return 0;
+ }
+ p->pcm_buffer_size = new_buffer_size;
+ p->pcm_buffer = new_buffer;
+ }
+
/* NOTE: This logic assumes that "short int" is 16-bits
* on all platforms. It happens to be for all that I know
* about.
@@ -728,7 +874,7 @@
* lsx_malloc()'ing a smaller buffer and call a consistent
* interface.
*/
- buffer_l = lsx_malloc(nsamples * sizeof(short signed int));
+ buffer_l = p->pcm_buffer;
if (ft->signal.channels == 2)
{
@@ -735,7 +881,7 @@
/* lame doesn't support iterleaved samples so we must break
* them out into seperate buffers.
*/
- buffer_r = lsx_malloc(nsamples* sizeof(short signed int));
+ buffer_r = p->pcm_buffer + nsamples;
j=0;
for (i=0; i<nsamples; i++)
{
@@ -750,59 +896,55 @@
buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips);
}
- mp3buffer_size = 1.25 * nsamples + 7200;
- mp3buffer = lsx_malloc(mp3buffer_size);
+ new_buffer_size = LAME_BUFFER_SIZE(samp);
+ if (p->mp3_buffer_size < new_buffer_size) {
+ unsigned char *new_buffer = lsx_realloc(p->mp3_buffer, new_buffer_size);
+ if (!new_buffer) {
+ lsx_fail_errno(ft, SOX_ENOMEM, "Out of memory");
+ return 0;
+ }
+ p->mp3_buffer_size = new_buffer_size;
+ p->mp3_buffer = new_buffer;
+ }
if ((written =
- p->lame_encode_buffer(p->gfp,buffer_l, buffer_r,
- nsamples, mp3buffer,
- (int)mp3buffer_size)) > mp3buffer_size){
+ p->lame_encode_buffer(p->gfp,buffer_l, buffer_r,
+ nsamples, p->mp3_buffer,
+ (int)p->mp3_buffer_size)) > p->mp3_buffer_size){
lsx_fail_errno(ft,SOX_EOF,"Encoding failed");
- goto end;
+ return 0;
}
- if (lsx_writebuf(ft, mp3buffer, written) < written)
+ if (lsx_writebuf(ft, p->mp3_buffer, written) < written)
{
lsx_fail_errno(ft,SOX_EOF,"File write failed");
- goto end;
+ return 0;
}
- done = nsamples*ft->signal.channels;
-
-end:
- free(mp3buffer);
- if (ft->signal.channels == 2)
- free(buffer_r);
- free(buffer_l);
-
- return done;
+ return samp;
}
static int stopwrite(sox_format_t * ft)
{
priv_t *p = (priv_t *) ft->priv;
- unsigned char mp3buffer[7200];
- int written = p->lame_encode_flush(p->gfp, mp3buffer, (int)sizeof(mp3buffer));
+ unsigned long num_samples = ft->olength == SOX_IGNORE_LENGTH ? 0 : ft->olength / max(ft->signal.channels, 1);
+ int written = p->lame_encode_flush(p->gfp, p->mp3_buffer, (int)p->mp3_buffer_size);
if (written < 0)
lsx_fail_errno(ft, SOX_EOF, "Encoding failed");
- else if (lsx_writebuf(ft, mp3buffer, (size_t)written) < (size_t)written)
- {
+ else if (lsx_writebuf(ft, p->mp3_buffer, (size_t)written) < (size_t)written)
lsx_fail_errno(ft, SOX_EOF, "File write failed");
- }
- else if (p->lame_get_bWriteVbrTag(p->gfp) && ft->seekable) {
- p->lame_mp3_tags_fid(p->gfp, ft->fp);
- }
+ else if (ft->seekable && (num_samples != p->num_samples || p->vbr_tag))
+ rewrite_tags(ft, num_samples);
+ free(p->mp3_buffer);
+ free(p->pcm_buffer);
p->lame_close(p->gfp);
-#if defined HAVE_LIBLTDL && defined DL_LAME
- if (!lt_dlclose(p->lame_lth))
- lt_dlexit();
-#endif
+ LSX_DLLIBRARY_CLOSE(p, lame_dl);
return SOX_SUCCESS;
}
-#else /* HAVE_LAME_LAME_H */
+#else /* HAVE_LAME */
static int startwrite(sox_format_t * ft UNUSED)
{
lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support");
@@ -810,7 +952,7 @@
}
#define sox_mp3write NULL
#define stopwrite NULL
-#endif /* HAVE_LAME_LAME_H */
+#endif /* HAVE_LAME */
LSX_FORMAT_HANDLER(mp3)
{
@@ -825,4 +967,4 @@
};
return &handler;
}
-#endif /* defined(HAVE_LAME_LAME_H) || defined (HAVE_MAD_H) */
+#endif /* defined(HAVE_MAD_H) || defined(HAVE_LAME) */
--- a/src/sndfile.c
+++ b/src/sndfile.c
@@ -30,10 +30,49 @@
#define LOG_MAX 2048 /* As per the SFC_GET_LOG_INFO example */
-#ifndef HACKED_LSF
-#define sf_stop(x)
+#if !defined(HAVE_LIBLTDL) || !defined(HAVE_SNDFILE_1_0_12)
+#undef DL_SNDFILE
#endif
+static const char* const sndfile_library_names[] =
+{
+#ifdef DL_SNDFILE
+ "libsndfile-1",
+ "cygsndfile-1",
+#endif
+ NULL
+};
+
+#ifdef DL_SNDFILE
+ #define SNDFILE_FUNC LSX_DLENTRY_DYNAMIC
+ #define SNDFILE_FUNC_STOP LSX_DLENTRY_STUB
+#else
+ #define SNDFILE_FUNC LSX_DLENTRY_STATIC
+#ifdef HACKED_LSF
+ #define SNDFILE_FUNC_STOP LSX_DLENTRY_STATIC
+#else
+ #define SNDFILE_FUNC_STOP LSX_DLENTRY_STUB
+#endif
+#endif /* DL_SNDFILE */
+
+#ifdef HAVE_SNDFILE_1_0_12
+#define SNDFILE_FUNC_OPEN(f,x) \
+ SNDFILE_FUNC(f,x, SNDFILE*, sf_open_virtual, (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data))
+#else
+ SNDFILE_FUNC(f,x, SNDFILE*, sf_open_fd, (int fd, int mode, SF_INFO *sfinfo, int close_desc))
+#endif
+
+#define SNDFILE_FUNC_ENTRIES(f,x) \
+ SNDFILE_FUNC_OPEN(f,x) \
+ SNDFILE_FUNC_STOP(f,x, int, sf_stop, (SNDFILE *sndfile)) \
+ SNDFILE_FUNC(f,x, int, sf_close, (SNDFILE *sndfile)) \
+ SNDFILE_FUNC(f,x, int, sf_format_check, (const SF_INFO *info)) \
+ SNDFILE_FUNC(f,x, int, sf_command, (SNDFILE *sndfile, int command, void *data, int datasize)) \
+ SNDFILE_FUNC(f,x, sf_count_t, sf_read_int, (SNDFILE *sndfile, int *ptr, sf_count_t items)) \
+ SNDFILE_FUNC(f,x, sf_count_t, sf_write_int, (SNDFILE *sndfile, const int *ptr, sf_count_t items)) \
+ SNDFILE_FUNC(f,x, sf_count_t, sf_seek, (SNDFILE *sndfile, sf_count_t frames, int whence)) \
+ SNDFILE_FUNC(f,x, const char*, sf_strerror, (SNDFILE *sndfile))
+
/* Private data for sndfile files */
typedef struct {
SNDFILE *sf_file;
@@ -40,6 +79,7 @@
SF_INFO *sf_info;
char * log_buffer;
char const * log_buffer_ptr;
+ LSX_DLENTRIES_TO_PTRS(SNDFILE_FUNC_ENTRIES, sndfile_dl);
} priv_t;
/*
@@ -48,7 +88,7 @@
static void drain_log_buffer(sox_format_t * ft)
{
priv_t * sf = (priv_t *)ft->priv;
- sf_command(sf->sf_file, SFC_GET_LOG_INFO, sf->log_buffer, LOG_MAX);
+ sf->sf_command(sf->sf_file, SFC_GET_LOG_INFO, sf->log_buffer, LOG_MAX);
while (*sf->log_buffer_ptr) {
static char const warning_prefix[] = "*** Warning : ";
char const * end = strchr(sf->log_buffer_ptr, '\n');
@@ -181,6 +221,49 @@
{ "xi", SF_FORMAT_XI }
};
+static int sf_stop_stub(SNDFILE *sndfile)
+{
+ return 1;
+}
+
+#ifdef HAVE_SNDFILE_1_0_12
+
+static sf_count_t vio_get_filelen(void *user_data)
+{
+ return lsx_filelength((sox_format_t *)user_data);
+}
+
+static sf_count_t vio_seek(sf_count_t offset, int whence, void *user_data)
+{
+ return lsx_seeki((sox_format_t *)user_data, (off_t)offset, whence);
+}
+
+static sf_count_t vio_read(void *ptr, sf_count_t count, void *user_data)
+{
+ return lsx_readbuf((sox_format_t *)user_data, ptr, (size_t)count);
+}
+
+static sf_count_t vio_write(const void *ptr, sf_count_t count, void *user_data)
+{
+ return lsx_writebuf((sox_format_t *)user_data, ptr, (size_t)count);
+}
+
+static sf_count_t vio_tell(void *user_data)
+{
+ return lsx_tell((sox_format_t *)user_data);
+}
+
+static SF_VIRTUAL_IO vio =
+{
+ vio_get_filelen,
+ vio_seek,
+ vio_read,
+ vio_write,
+ vio_tell
+};
+
+#endif /* HAVE_SNDFILE_1_0_12 */
+
/* Convert file name or type to libsndfile format */
static int name_to_format(const char *name)
{
@@ -205,10 +288,22 @@
return 0;
}
-static void start(sox_format_t * ft)
+static int start(sox_format_t * ft)
{
priv_t * sf = (priv_t *)ft->priv;
int subtype = ft_enc(ft->encoding.bits_per_sample? ft->encoding.bits_per_sample : ft->signal.precision, ft->encoding.encoding);
+ int open_library_result;
+
+ LSX_DLLIBRARY_OPEN(
+ sf,
+ sndfile_dl,
+ SNDFILE_FUNC_ENTRIES,
+ "libsndfile library",
+ sndfile_library_names,
+ open_library_result);
+ if (open_library_result)
+ return SOX_EOF;
+
sf->log_buffer_ptr = sf->log_buffer = lsx_malloc((size_t)LOG_MAX);
sf->sf_info = lsx_calloc(1, sizeof(SF_INFO));
@@ -219,16 +314,18 @@
else
sf->sf_info->format = name_to_format(ft->filetype) | subtype;
}
- sf->sf_info->samplerate = ft->signal.rate;
+ sf->sf_info->samplerate = (int)ft->signal.rate;
sf->sf_info->channels = ft->signal.channels;
if (ft->signal.channels)
sf->sf_info->frames = ft->signal.length / ft->signal.channels;
+
+ return SOX_SUCCESS;
}
static int check_read_params(sox_format_t * ft, unsigned channels,
sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample, off_t length)
{
- ft->signal.length = length;
+ ft->signal.length = (size_t)length;
if (channels && ft->signal.channels && ft->signal.channels != channels)
lsx_warn("`%s': overriding number of channels", ft->filename);
@@ -262,15 +359,20 @@
sox_encoding_t encoding;
sox_rate_t rate;
- start(ft);
+ if (start(ft) == SOX_EOF)
+ return SOX_EOF;
- sf->sf_file = sf_open_fd(fileno(ft->fp), SFM_READ, sf->sf_info, 1);
+#ifdef HAVE_SNDFILE_1_0_12
+ sf->sf_file = sf->sf_open_virtual(&vio, SFM_READ, sf->sf_info, ft);
+#else
+ sf->sf_file = sf->sf_open_fd(fileno(ft->fp), SFM_READ, sf->sf_info, 1);
ft->fp = NULL; /* Transfer ownership of fp to LSF */
+#endif
drain_log_buffer(ft);
if (sf->sf_file == NULL) {
memset(ft->sox_errstr, 0, sizeof(ft->sox_errstr));
- strncpy(ft->sox_errstr, sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
+ strncpy(ft->sox_errstr, sf->sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
free(sf->sf_file);
return SOX_EOF;
}
@@ -289,8 +391,8 @@
#ifdef HAVE_SFC_SET_SCALE_FLOAT_INT_READ
if ((sf->sf_info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) {
- sf_command(sf->sf_file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
- sf_command(sf->sf_file, SFC_SET_CLIPPING, NULL, SF_TRUE);
+ sf->sf_command(sf->sf_file, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE);
+ sf->sf_command(sf->sf_file, SFC_SET_CLIPPING, NULL, SF_TRUE);
}
#endif
@@ -310,7 +412,7 @@
{
priv_t * sf = (priv_t *)ft->priv;
/* FIXME: We assume int == sox_sample_t here */
- return (size_t)sf_read_int(sf->sf_file, (int *)buf, (sf_count_t)len);
+ return (size_t)sf->sf_read_int(sf->sf_file, (int *)buf, (sf_count_t)len);
}
/*
@@ -319,9 +421,10 @@
static int stopread(sox_format_t * ft)
{
priv_t * sf = (priv_t *)ft->priv;
- sf_stop(sf->sf_file);
+ sf->sf_stop(sf->sf_file);
drain_log_buffer(ft);
- sf_close(sf->sf_file);
+ sf->sf_close(sf->sf_file);
+ LSX_DLLIBRARY_CLOSE(sf, sndfile_dl);
return SOX_SUCCESS;
}
@@ -328,16 +431,19 @@
static int startwrite(sox_format_t * ft)
{
priv_t * sf = (priv_t *)ft->priv;
- start(ft);
+
+ if (start(ft) == SOX_EOF)
+ return SOX_EOF;
+
/* If output format is invalid, try to find a sensible default */
- if (!sf_format_check(sf->sf_info)) {
+ if (!sf->sf_format_check(sf->sf_info)) {
SF_FORMAT_INFO format_info;
int i, count;
- sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT_COUNT, &count, (int) sizeof(int));
+ sf->sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT_COUNT, &count, (int) sizeof(int));
for (i = 0; i < count; i++) {
format_info.format = i;
- sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT, &format_info, (int) sizeof(format_info));
+ sf->sf_command(sf->sf_file, SFC_GET_SIMPLE_FORMAT, &format_info, (int) sizeof(format_info));
if ((format_info.format & SF_FORMAT_TYPEMASK) == (sf->sf_info->format & SF_FORMAT_TYPEMASK)) {
sf->sf_info->format = format_info.format;
/* FIXME: Print out exactly what we chose, needs sndfile ->
@@ -346,7 +452,7 @@
}
}
- if (!sf_format_check(sf->sf_info)) {
+ if (!sf->sf_format_check(sf->sf_info)) {
lsx_fail("cannot find a usable output encoding");
return SOX_EOF;
}
@@ -354,13 +460,17 @@
lsx_warn("cannot use desired output encoding, choosing default");
}
- sf->sf_file = sf_open_fd(fileno(ft->fp), SFM_WRITE, sf->sf_info, 1);
+#ifdef HAVE_SNDFILE_1_0_12
+ sf->sf_file = sf->sf_open_virtual(&vio, SFM_WRITE, sf->sf_info, ft);
+#else
+ sf->sf_file = sf->sf_open_fd(fileno(ft->fp), SFM_WRITE, sf->sf_info, 1);
ft->fp = NULL; /* Transfer ownership of fp to LSF */
+#endif
drain_log_buffer(ft);
if (sf->sf_file == NULL) {
memset(ft->sox_errstr, 0, sizeof(ft->sox_errstr));
- strncpy(ft->sox_errstr, sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
+ strncpy(ft->sox_errstr, sf->sf_strerror(sf->sf_file), sizeof(ft->sox_errstr)-1);
free(sf->sf_file);
return SOX_EOF;
}
@@ -367,7 +477,7 @@
#ifdef HAVE_SFC_SET_SCALE_INT_FLOAT_WRITE
if ((sf->sf_info->format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT)
- sf_command(sf->sf_file, SFC_SET_SCALE_INT_FLOAT_WRITE, NULL, SF_TRUE);
+ sf->sf_command(sf->sf_file, SFC_SET_SCALE_INT_FLOAT_WRITE, NULL, SF_TRUE);
#endif
return SOX_SUCCESS;
@@ -381,7 +491,7 @@
{
priv_t * sf = (priv_t *)ft->priv;
/* FIXME: We assume int == sox_sample_t here */
- return (size_t)sf_write_int(sf->sf_file, (int *)buf, (sf_count_t)len);
+ return (size_t)sf->sf_write_int(sf->sf_file, (int *)buf, (sf_count_t)len);
}
/*
@@ -390,9 +500,10 @@
static int stopwrite(sox_format_t * ft)
{
priv_t * sf = (priv_t *)ft->priv;
- sf_stop(sf->sf_file);
+ sf->sf_stop(sf->sf_file);
drain_log_buffer(ft);
- sf_close(sf->sf_file);
+ sf->sf_close(sf->sf_file);
+ LSX_DLLIBRARY_CLOSE(sf, sndfile_dl);
return SOX_SUCCESS;
}
@@ -399,7 +510,7 @@
static int seek(sox_format_t * ft, uint64_t offset)
{
priv_t * sf = (priv_t *)ft->priv;
- sf_seek(sf->sf_file, (sf_count_t)(offset / ft->signal.channels), SEEK_CUR);
+ sf->sf_seek(sf->sf_file, (sf_count_t)(offset / ft->signal.channels), SEEK_CUR);
return SOX_SUCCESS;
}
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -133,11 +133,6 @@
#define LSX_MAX_TBW3A floor(LSX_MAX_TBW0A * LSX_TO_3dB)
void lsx_plot_fir(double * h, int num_points, sox_rate_t rate, sox_plot_t type, char const * title, double y1, double y2);
-#ifndef HAVE_STRCASECMP
-int strcasecmp(const char *s1, const char *s2);
-int strncasecmp(char const * s1, char const * s2, size_t n);
-#endif
-
#ifdef HAVE_BYTESWAP_H
#include <byteswap.h>
#define lsx_swapw(x) bswap_16(x)
@@ -309,5 +304,98 @@
int lsx_effects_init(void);
int lsx_effects_quit(void);
+
+/*--------------------------------- Dynamic Library ----------------------------------*/
+
+#if defined(HAVE_LIBLTDL)
+ #include <ltdl.h>
+ typedef lt_dlhandle lsx_dlhandle;
+#else
+ struct lsx_dlhandle_tag;
+ typedef struct lsx_dlhandle_tag *lsx_dlhandle;
+#endif
+
+typedef void (*lsx_dlptr)(void);
+
+typedef struct lsx_dlfunction_info
+{
+ const char* name;
+ lsx_dlptr static_func;
+ lsx_dlptr stub_func;
+} lsx_dlfunction_info;
+
+int lsx_open_dllibrary(
+ const char* library_description,
+ const char * const library_names[],
+ const lsx_dlfunction_info func_infos[],
+ lsx_dlptr selected_funcs[],
+ lsx_dlhandle* pdl);
+
+void lsx_close_dllibrary(
+ lsx_dlhandle dl);
+
+#define LSX_DLENTRIES_APPLY__(entries, f, x) entries(f, x)
+
+#define LSX_DLENTRY_TO_PTR__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+ func_return (*func_ptr) func_args;
+
+/* LSX_DLENTRIES_TO_PTRS: Given an ENTRIES macro and the name of the dlhandle
+ variable, declares the corresponding function pointer variables and the
+ dlhandle variable. */
+#define LSX_DLENTRIES_TO_PTRS(entries, dlhandle) \
+ LSX_DLENTRIES_APPLY__(entries, LSX_DLENTRY_TO_PTR__, 0) \
+ lsx_dlhandle dlhandle
+
+#define LSX_DLLIBRARY_OPEN1__(unused, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+ { #func_name, (lsx_dlptr)(static_func), (lsx_dlptr)(stub_func) },
+
+#define LSX_DLLIBRARY_OPEN2__(ptr_container, func_return, func_name, func_args, static_func, stub_func, func_ptr) \
+ (ptr_container)->func_ptr = (func_return (*)func_args)lsx_dlfunction_open_library_funcs[lsx_dlfunction_open_library_index++];
+
+/* LSX_DLFUNCTION_OPEN_LIBRARY: Input an ENTRIES macro, the library's description,
+ a null-terminated list of library names (i.e. { "libmp3-0", "libmp3", NULL }),
+ the name of the dlhandle variable, the name of the structure that contains
+ the function pointer and dlhandle variables, and the name of the variable in
+ which the result of the lsx_open_dllibrary call should be stored. This will
+ call lsx_open_dllibrary and copy the resulting function pointers into the
+ structure members. */
+#define LSX_DLLIBRARY_OPEN(ptr_container, dlhandle, entries, library_description, library_names, return_var) \
+ do { \
+ lsx_dlfunction_info lsx_dlfunction_open_library_infos[] = { \
+ LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN1__, 0) \
+ {NULL,NULL,NULL} }; \
+ int lsx_dlfunction_open_library_index = 0; \
+ lsx_dlptr lsx_dlfunction_open_library_funcs[sizeof(lsx_dlfunction_open_library_infos)/sizeof(lsx_dlfunction_open_library_infos[0])]; \
+ (return_var) = lsx_open_dllibrary((library_description), (library_names), lsx_dlfunction_open_library_infos, lsx_dlfunction_open_library_funcs, &(ptr_container)->dlhandle); \
+ LSX_DLENTRIES_APPLY__(entries, LSX_DLLIBRARY_OPEN2__, ptr_container) \
+ } while(0)
+
+#define LSX_DLLIBRARY_CLOSE(ptr_container, dlhandle) \
+ lsx_close_dllibrary((ptr_container)->dlhandle)
+
+ /* LSX_DLENTRY_STATIC: For use in creating an ENTRIES macro. func is
+ expected to be available at link time. If not present, link will fail. */
+#define LSX_DLENTRY_STATIC(f,x, ret, func, args) f(x, ret, func, args, func, NULL, func)
+
+ /* LSX_DLENTRY_DYNAMIC: For use in creating an ENTRIES macro. func need
+ not be available at link time (and if present, the link time version will
+ not be used). func will be loaded via dlsym. If this function is not
+ found in the shared library, the shared library will not be used. */
+#define LSX_DLENTRY_DYNAMIC(f,x, ret, func, args) f(x, ret, func, args, NULL, NULL, func)
+
+ /* LSX_DLENTRY_STUB: For use in creating an ENTRIES macro. func need not
+ be available at link time (and if present, the link time version will not
+ be used). If using DL_LAME, the func may be loaded via dlopen/dlsym, but
+ if not found, the shared library will still be used if all of the
+ non-stub functions are found. If the function is not found via dlsym (or
+ if we are not loading any shared libraries), the stub will be used. This
+ assumes that the name of the stub function is the name of the function +
+ "_stub". */
+#define LSX_DLENTRY_STUB(f,x, ret, func, args) f(x, ret, func, args, NULL, func##_stub, func)
+
+ /* LSX_DLFUNC_IS_STUB: returns true if the named function is a do-nothing
+ stub. Assumes that the name of the stub function is the name of the
+ function + "_stub". */
+#define LSX_DLFUNC_IS_STUB(ptr_container, func) ((ptr_container)->func == func##_stub)
#endif
--- a/src/util.c
+++ b/src/util.c
@@ -21,21 +21,31 @@
#include <ctype.h>
#include <stdio.h>
-#ifndef HAVE_STRCASECMP
-int strcasecmp(const char * s1, const char * s2)
+int lsx_strcasecmp(const char * s1, const char * s2)
{
+#if defined(HAVE_STRCASECMP)
+ return strcasecmp(s1, s2);
+#elif defined(_MSC_VER)
+ return _stricmp(s1, s2);
+#else
while (*s1 && (toupper(*s1) == toupper(*s2)))
s1++, s2++;
return toupper(*s1) - toupper(*s2);
+#endif
}
-int strncasecmp(char const * s1, char const * s2, size_t n)
+int lsx_strncasecmp(char const * s1, char const * s2, size_t n)
{
+#if defined(HAVE_STRCASECMP)
+ return strncasecmp(s1, s2, n);
+#elif defined(_MSC_VER)
+ return _strnicmp(s1, s2, n);
+#else
while (--n && *s1 && (toupper(*s1) == toupper(*s2)))
s1++, s2++;
return toupper(*s1) - toupper(*s2);
-}
#endif
+}
sox_bool lsx_strends(char const * str, char const * end)
{
@@ -136,3 +146,138 @@
return string[n];
}
+int lsx_open_dllibrary(
+ const char* library_description,
+ const char* const library_names[] UNUSED,
+ const lsx_dlfunction_info func_infos[],
+ lsx_dlptr selected_funcs[],
+ lsx_dlhandle* pdl)
+{
+ int failed = 0;
+ lsx_dlhandle dl = NULL;
+
+ /* Track enough information to give a good error message about one failure.
+ * Let failed symbol load override failed library open, and let failed
+ * library open override missing static symbols.
+ */
+ const char* failed_libname = NULL;
+ const char* failed_funcname = NULL;
+
+#ifdef HAVE_LIBLTDL
+ if (library_names && library_names[0])
+ {
+ const char* const* libname;
+ if (lt_dlinit())
+ {
+ lsx_fail(
+ "Unable to load %s - failed to initialize ltdl.",
+ library_description);
+ return 1;
+ }
+
+ for (libname = library_names; *libname; libname++)
+ {
+ dl = lt_dlopenext(*libname);
+ if (dl)
+ {
+ size_t i;
+ for (i = 0; func_infos[i].name; i++)
+ {
+ union {lsx_dlptr fn; lt_ptr ptr;} func;
+ func.ptr = lt_dlsym(dl, func_infos[i].name);
+ selected_funcs[i] = func.fn ? func.fn : func_infos[i].stub_func;
+ if (!selected_funcs[i])
+ {
+ lt_dlclose(dl);
+ dl = NULL;
+ failed_libname = *libname;
+ failed_funcname = func_infos[i].name;
+ break;
+ }
+ }
+
+ if (dl)
+ break;
+ }
+ else if (!failed_libname)
+ {
+ failed_libname = *libname;
+ }
+ }
+
+ if (!dl)
+ lt_dlexit();
+ }
+#endif /* HAVE_LIBLTDL */
+
+ if (!dl)
+ {
+ size_t i;
+ for (i = 0; func_infos[i].name; i++)
+ {
+ selected_funcs[i] =
+ func_infos[i].static_func
+ ? func_infos[i].static_func
+ : func_infos[i].stub_func;
+ if (!selected_funcs[i])
+ {
+ if (!failed_libname)
+ {
+ failed_libname = "static";
+ failed_funcname = func_infos[i].name;
+ }
+
+ failed = 1;
+ break;
+ }
+ }
+ }
+
+ if (failed)
+ {
+ size_t i;
+ for (i = 0; func_infos[i].name; i++)
+ selected_funcs[i] = NULL;
+#ifdef HAVE_LIBLTDL
+#define LTDL_MISSING ""
+#else
+#define LTDL_MISSING " (Dynamic library support not configured.)"
+#endif /* HAVE_LIBLTDL */
+ if (failed_funcname)
+ {
+ lsx_fail(
+ "Unable to load %s (%s) function \"%s\"." LTDL_MISSING,
+ library_description,
+ failed_libname,
+ failed_funcname);
+ }
+ else if (failed_libname)
+ {
+ lsx_fail(
+ "Unable to load %s (%s)." LTDL_MISSING,
+ library_description,
+ failed_libname);
+ }
+ else
+ {
+ lsx_fail(
+ "Unable to load %s - no dynamic library names selected." LTDL_MISSING,
+ library_description);
+ }
+ }
+
+ *pdl = dl;
+ return failed;
+}
+
+void lsx_close_dllibrary(
+ lsx_dlhandle dl UNUSED)
+{
+#ifdef HAVE_LIBLTDL
+ if (dl)
+ {
+ lt_dlclose(dl);
+ lt_dlexit();
+ }
+#endif /* HAVE_LIBLTDL */
+}
--- a/src/util.h
+++ b/src/util.h
@@ -121,3 +121,8 @@
#define dB_to_linear(x) exp((x) * M_LN10 * 0.05)
#define linear_to_dB(x) (log10(x) * 20)
+
+#ifndef HAVE_STRCASECMP
+#define strcasecmp(s1, s2) lsx_strcasecmp((s1), (s2))
+#define strncasecmp(s1, s2, n) lsx_strncasecmp((s1), (s2), (n))
+#endif