ref: 1e097282146bc0eeb0008f970689f9fdb281d141
dir: /src/mp3.c/
/* MP3 support for SoX * * Uses libmad for MP3 decoding * and libmp3lame for MP3 encoding * * Written by Fabrizio Gennari <fabrizio.ge@tiscali.it> * * The decoding part is based on the decoder-tutorial program madlld * written by Bertrand Petit <madlld@phoe.fmug.org>, */ #include "sox_i.h" #include <string.h> #if defined(HAVE_LAME_LAME_H) || defined(HAVE_LAME_H) #define HAVE_LAME 1 #else #undef HAVE_LAME #endif #if defined(HAVE_MAD_H) || defined(HAVE_LAME) #ifdef HAVE_MAD_H #include <mad.h> #endif #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)) #define USING_ID3TAG 1 #endif #ifdef USING_ID3TAG #include <id3tag.h> #if defined(HAVE_UNISTD_H) #include <unistd.h> #elif defined(HAVE_IO_H) #include <io.h> #endif #else #define ID3_TAG_FLAG_FOOTERPRESENT 0x10 #endif #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 }; #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_ID3TAG_SET_FIELDVALUE #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 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; ptrdiff_t cursamp; size_t FrameCount; LSX_DLENTRIES_TO_PTRS(MAD_FUNC_ENTRIES, mad_dl); #endif /*HAVE_MAD_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() from MAD's libid3tag, so we don't have to link to it Returns 0 if the frame is not an ID3 tag, tag length if it is */ static int tagtype(const unsigned char *data, size_t length) { if (length >= 3 && data[0] == 'T' && data[1] == 'A' && data[2] == 'G') { return 128; /* ID3V1 */ } if (length >= 10 && (data[0] == 'I' && data[1] == 'D' && data[2] == '3') && data[3] < 0xff && data[4] < 0xff && data[6] < 0x80 && data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80) { /* ID3V2 */ unsigned char flags; unsigned int size; flags = data[5]; size = 10 + (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9]; if (flags & ID3_TAG_FLAG_FOOTERPRESENT) size += 10; for (; size < length && !data[size]; ++size); /* Consume padding */ return size; } return 0; } #include "mp3-util.h" #ifdef HAVE_MAD_H /* * (Re)fill the stream buffer that is to be decoded. If any data * still exists in the buffer then they are first shifted to be * front of the stream buffer. */ static int sox_mp3_input(sox_format_t * ft) { priv_t *p = (priv_t *) ft->priv; size_t bytes_read; size_t remaining; remaining = p->Stream.bufend - p->Stream.next_frame; /* libmad does not consume all the buffer it's given. Some * data, part of a truncated frame, is left unused at the * end of the buffer. That data must be put back at the * beginning of the buffer and taken in account for * refilling the buffer. This means that the input buffer * must be large enough to hold a complete frame at the * highest observable bit-rate (currently 448 kb/s). * TODO: Is 2016 bytes the size of the largest frame? * (448000*(1152/32000))/8 */ memmove(p->mp3_buffer, p->Stream.next_frame, 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->mp3_buffer, bytes_read+remaining); p->Stream.error = 0; return SOX_SUCCESS; } /* Attempts to read an ID3 tag at the current location in stream and * consume it all. Returns SOX_EOF if no tag is found. Its up to * caller to recover. * */ static int sox_mp3_inputtag(sox_format_t * ft) { priv_t *p = (priv_t *) ft->priv; int rc = SOX_EOF; size_t remaining; size_t tagsize; /* FIXME: This needs some more work if we are to ever * look at the ID3 frame. This is because the Stream * may not be able to hold the complete ID3 frame. * We should consume the whole frame inside tagtype() * instead of outside of tagframe(). That would support * recovering when Stream contains less then 8-bytes (header) * and also when ID3v2 is bigger then Stream buffer size. * Need to pass in stream so that buffer can be * consumed as well as letting additional data to be * read in. */ remaining = p->Stream.bufend - p->Stream.next_frame; if ((tagsize = tagtype(p->Stream.this_frame, remaining))) { p->mad_stream_skip(&p->Stream, tagsize); rc = SOX_SUCCESS; } /* We know that a valid frame hasn't been found yet * so help libmad out and go back into frame seek mode. * This is true whether an ID3 tag was found or not. */ p->mad_stream_sync(&p->Stream); return rc; } static int startread(sox_format_t * ft) { priv_t *p = (priv_t *) ft->priv; size_t ReadSize; sox_bool ignore_length = ft->signal.length == SOX_IGNORE_LENGTH; int open_library_result; 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; p->mp3_buffer_size = sox_globals.bufsiz; p->mp3_buffer = lsx_malloc(p->mp3_buffer_size); ft->signal.length = SOX_UNSPEC; if (ft->seekable) { #ifdef USING_ID3TAG read_comments(ft); rewind(ft->fp); if (!ft->signal.length) #endif 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); 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->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->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; continue; } /* 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; } 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; } p->FrameCount=1; 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! */ } p->cursamp = 0; return SOX_SUCCESS; } /* * Read up to len samples from p->Synth * If needed, read some more MP3 data, decode them and synth them * Place in buf[]. * Return number of samples read. */ static size_t sox_mp3read(sox_format_t * ft, sox_sample_t *buf, size_t len) { priv_t *p = (priv_t *) ft->priv; size_t donow,i,done=0; mad_fixed_t sample; size_t chan; do { size_t x = (p->Synth.pcm.length - p->cursamp)*ft->signal.channels; donow=min(len, x); i=0; while(i<donow){ for(chan=0;chan<ft->signal.channels;chan++){ sample=p->Synth.pcm.samples[chan][p->cursamp]; if (sample < -MAD_F_ONE) sample=-MAD_F_ONE; else if (sample >= MAD_F_ONE) sample=MAD_F_ONE-1; *buf++=(sox_sample_t)(sample<<(32-1-MAD_F_FRACBITS)); i++; } p->cursamp++; }; len-=donow; done+=donow; if (len==0) break; /* check whether input buffer needs a refill */ if (p->Stream.error == MAD_ERROR_BUFLEN) { if (sox_mp3_input(ft) == SOX_EOF) { lsx_debug("sox_mp3_input EOF"); break; } } if (p->mad_frame_decode(&p->Frame,&p->Stream)) { if(MAD_RECOVERABLE(p->Stream.error)) { sox_mp3_inputtag(ft); continue; } else { if (p->Stream.error == MAD_ERROR_BUFLEN) continue; else { lsx_report("unrecoverable frame level error (%s).", p->mad_stream_errorstr(&p->Stream)); break; } } } p->FrameCount++; p->mad_timer_add(&p->Timer,p->Frame.header.duration); p->mad_synth_frame(&p->Synth,&p->Frame); p->cursamp=0; } while(1); return done; } static int stopread(sox_format_t * ft) { priv_t *p=(priv_t*) ft->priv; mad_synth_finish(&p->Synth); p->mad_frame_finish(&p->Frame); p->mad_stream_finish(&p->Stream); free(p->mp3_buffer); LSX_DLLIBRARY_CLOSE(p, mad_dl); return SOX_SUCCESS; } #else /*HAVE_MAD_H*/ static int startread(sox_format_t * ft) { lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 decoding support"); return SOX_EOF; } #define sox_mp3read NULL #define stopread NULL #endif /*HAVE_MAD_H*/ #ifdef HAVE_LAME /* Adapters for lame message callbacks: */ static void errorf(const char* fmt, va_list va) { sox_globals.subsystem=__FILE__; if (sox_globals.output_message_handler) (*sox_globals.output_message_handler)(1,sox_globals.subsystem,fmt,va); return; } static void debugf(const char* fmt, va_list va) { sox_globals.subsystem=__FILE__; if (sox_globals.output_message_handler) (*sox_globals.output_message_handler)(4,sox_globals.subsystem,fmt,va); return; } static void msgf(const char* fmt, va_list va) { sox_globals.subsystem=__FILE__; if (sox_globals.output_message_handler) (*sox_globals.output_message_handler)(3,sox_globals.subsystem,fmt,va); return; } /* 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; FILE *fp = ft->fp; size_t bytes_read; int id3v2_size; unsigned char id3v2_header[10]; if (fseeko(fp, 0, SEEK_SET) != 0) { lsx_warn("cannot update id3 tag - failed to seek to beginning"); return SOX_EOF; } /* 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 */ } /* 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"); 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){ lsx_fail_errno(ft,SOX_EOF,"Initialization of LAME library failed"); return(SOX_EOF); } /* First set message callbacks so we don't miss any messages: */ p->lame_set_errorf(p->gfp,errorf); 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"); return(SOX_EOF); } } else 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); if (!LSX_DLFUNC_IS_STUB(p, id3tag_init)) write_comments(ft); /* 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). * * The second most important parameter is probably "quality" (really * performance), which allows balancing encoding speed vs. quality. * In LAME, 0 specifies highest quality but is very slow, while * 9 selects poor quality, but is fast. (5 is the default and 2 is * recommended as a good trade-off for high quality encodes.) * * 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 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 * size) is selected using a number from 0 to 9. Use a value of 0 for high * quality, larger files, and 9 for smaller files of lower quality. 4 is * the default. * * In order to squeeze the selection of VBR into the encoding.compression * float we use negative numbers to select VRR. -4.2 would select default * VBR encoding (size) with high quality (speed). One special case is 0, * which is a valid VBR encoding parameter but not a valid bitrate. * Compression value of 0 is always treated as a high quality vbr, as a * result both -0.2 and 0.2 are treated as highest quality VBR (size) and * high quality (speed). * * Note: It would have been nice to simply use low values, 0-9, to trigger * VBR mode, but 8 kbps is a valid bit rate, so negative values were * used instead. */ lsx_debug("-C option is %f", ft->encoding.compression); if (ft->encoding.compression == HUGE_VAL) { /* Do nothing, use defaults: */ lsx_report("using MP3 encoding defaults"); } else { 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 (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) { 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"); } if (p->lame_set_VBR_q(p->gfp, bitrate_q) < 0) { lsx_fail_errno(ft, SOX_EOF, "lame_set_VBR_q(%d) failed (should be between 0 and 9)", bitrate_q); return(SOX_EOF); } lsx_report("lame_set_VBR_q(%d)", bitrate_q); } else { if (p->lame_set_brate(p->gfp, bitrate_q) < 0) { lsx_fail_errno(ft, SOX_EOF, "lame_set_brate(%d) failed", bitrate_q); return(SOX_EOF); } lsx_report("lame_set_brate(%d)", bitrate_q); } /* Set Quality */ if (encoder_q < 0) { /* use default quality value */ lsx_report("using MP3 default quality"); } else { if (p->lame_set_quality(p->gfp, encoder_q) < 0) { lsx_fail_errno(ft, SOX_EOF, "lame_set_quality(%d) failed", encoder_q); return(SOX_EOF); } 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); } return(SOX_SUCCESS); } static size_t sox_mp3write(sox_format_t * ft, const sox_sample_t *buf, size_t samp) { priv_t *p = (priv_t *)ft->priv; size_t new_buffer_size; short signed int *buffer_l, *buffer_r = NULL; int nsamples = samp/ft->signal.channels; int i,j; 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. * * Lame ultimately wants data scaled to 16-bit samples * and assumes for the majority of cases that your passing * in something scaled based on passed in datatype * (16, 32, 64, and float). * * If we used long buffers then this means it expects * different scalling between 32-bit and 64-bit CPU's. * * We might as well scale it ourselfs to 16-bit to allow * lsx_malloc()'ing a smaller buffer and call a consistent * interface. */ buffer_l = p->pcm_buffer; if (ft->signal.channels == 2) { /* lame doesn't support iterleaved samples so we must break * them out into seperate buffers. */ buffer_r = p->pcm_buffer + nsamples; j=0; for (i=0; i<nsamples; i++) { buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips); buffer_r[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips); } } else { j=0; for (i=0; i<nsamples; i++) buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips); } 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, p->mp3_buffer, (int)p->mp3_buffer_size)) > p->mp3_buffer_size){ lsx_fail_errno(ft,SOX_EOF,"Encoding failed"); return 0; } if (lsx_writebuf(ft, p->mp3_buffer, written) < written) { lsx_fail_errno(ft,SOX_EOF,"File write failed"); return 0; } return samp; } static int stopwrite(sox_format_t * ft) { priv_t *p = (priv_t *) ft->priv; 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, p->mp3_buffer, (size_t)written) < (size_t)written) lsx_fail_errno(ft, SOX_EOF, "File write failed"); 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); LSX_DLLIBRARY_CLOSE(p, lame_dl); return SOX_SUCCESS; } #else /* HAVE_LAME */ static int startwrite(sox_format_t * ft UNUSED) { lsx_fail_errno(ft,SOX_EOF,"SoX was compiled without MP3 encoding support"); return SOX_EOF; } #define sox_mp3write NULL #define stopwrite NULL #endif /* HAVE_LAME */ LSX_FORMAT_HANDLER(mp3) { static char const * const names[] = {"mp3", "mp2", "audio/mpeg", NULL}; static unsigned const write_encodings[] = { SOX_ENCODING_MP3, 0, 0}; static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE, "MPEG Layer 3 lossy audio compression", names, 0, startread, sox_mp3read, stopread, startwrite, sox_mp3write, stopwrite, NULL, write_encodings, NULL, sizeof(priv_t) }; return &handler; } #endif /* defined(HAVE_MAD_H) || defined(HAVE_LAME) */