ref: 9b1f7f515d0173a16a263f2d16a0b3a541530177
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_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 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) */