mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-03-03 20:40:35 +00:00
added libraries: opus flac libsndfile updated: libvorbis libogg openal - Everything works as expected for now. Bare in mind libsndfile needed the check for whether or not it could find the xiph libraries removed in order for this to work.
784 lines
23 KiB
C
784 lines
23 KiB
C
/*
|
|
** Copyright (C) 2020 Arthur Taylor <art@ified.ca>
|
|
** Copyright (C) 2019 Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
**
|
|
** This program is free software ; you can redistribute it and/or modify
|
|
** it under the terms of the GNU Lesser General Public License as published by
|
|
** the Free Software Foundation ; either version 2.1 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY ; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU Lesser General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU Lesser General Public License
|
|
** along with this program ; if not, write to the Free Software
|
|
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "sfconfig.h"
|
|
#include "sndfile.h"
|
|
#include "common.h"
|
|
#include "mpeg.h"
|
|
|
|
|
|
#if HAVE_MPEG
|
|
|
|
#include <lame/lame.h>
|
|
|
|
/*
|
|
* RANT RANT RANT
|
|
*
|
|
* Lame has 11 functions for inputing sample data of various types and
|
|
* configurations, but due to bad definitions, or missing combinations, they
|
|
* aren't really of much help to us.
|
|
*
|
|
*/
|
|
|
|
typedef struct
|
|
{ lame_t lamef ;
|
|
unsigned char *block ;
|
|
size_t block_len ;
|
|
int frame_samples ;
|
|
double compression ;
|
|
int initialized ;
|
|
} MPEG_L3_ENC_PRIVATE ;
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------------------
|
|
** Private function prototypes.
|
|
*/
|
|
|
|
static int mpeg_l3_encoder_close (SF_PRIVATE *psf) ;
|
|
static int mpeg_l3_encoder_construct (SF_PRIVATE *psf) ;
|
|
static int mpeg_l3_encoder_byterate (SF_PRIVATE *psf) ;
|
|
|
|
static sf_count_t mpeg_l3_encode_write_short_stereo (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_int_stereo (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_float_stereo (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_double_stereo (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_short_mono (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_int_mono (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_float_mono (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
|
|
static sf_count_t mpeg_l3_encode_write_double_mono (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
|
|
|
|
/*-----------------------------------------------------------------------------------------------
|
|
** Exported functions.
|
|
*/
|
|
|
|
int
|
|
mpeg_l3_encoder_init (SF_PRIVATE *psf, int info_tag)
|
|
{ MPEG_L3_ENC_PRIVATE* pmpeg = NULL ;
|
|
|
|
if (psf->file.mode == SFM_RDWR)
|
|
return SFE_BAD_MODE_RW ;
|
|
|
|
if (psf->file.mode != SFM_WRITE)
|
|
return SFE_INTERNAL ;
|
|
|
|
psf->codec_data = pmpeg = calloc (1, sizeof (MPEG_L3_ENC_PRIVATE)) ;
|
|
if (!pmpeg)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
if (psf->sf.channels < 1 || psf->sf.channels > 2)
|
|
return SFE_BAD_OPEN_FORMAT ;
|
|
|
|
if (! (pmpeg->lamef = lame_init ()))
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
pmpeg->compression = -1.0 ; /* Unset */
|
|
|
|
lame_set_in_samplerate (pmpeg->lamef, psf->sf.samplerate) ;
|
|
lame_set_num_channels (pmpeg->lamef, psf->sf.channels) ;
|
|
if (lame_set_out_samplerate (pmpeg->lamef, psf->sf.samplerate) < 0)
|
|
return SFE_MPEG_BAD_SAMPLERATE ;
|
|
|
|
lame_set_write_id3tag_automatic (pmpeg->lamef, 0) ;
|
|
|
|
if (!info_tag || psf->is_pipe)
|
|
{ /* Can't seek back, so force disable Xing/Lame/Info header. */
|
|
lame_set_bWriteVbrTag (pmpeg->lamef, 0) ;
|
|
} ;
|
|
|
|
if (psf->sf.channels == 2)
|
|
{ psf->write_short = mpeg_l3_encode_write_short_stereo ;
|
|
psf->write_int = mpeg_l3_encode_write_int_stereo ;
|
|
psf->write_float = mpeg_l3_encode_write_float_stereo ;
|
|
psf->write_double = mpeg_l3_encode_write_double_stereo ;
|
|
}
|
|
else
|
|
{ psf->write_short = mpeg_l3_encode_write_short_mono ;
|
|
psf->write_int = mpeg_l3_encode_write_int_mono ;
|
|
psf->write_float = mpeg_l3_encode_write_float_mono ;
|
|
psf->write_double = mpeg_l3_encode_write_double_mono ;
|
|
}
|
|
|
|
psf->sf.seekable = 0 ;
|
|
psf->codec_close = mpeg_l3_encoder_close ;
|
|
psf->byterate = mpeg_l3_encoder_byterate ;
|
|
psf->datalength = 0 ;
|
|
|
|
return 0 ;
|
|
} /* mpeg_l3_encoder_init */
|
|
|
|
int
|
|
mpeg_l3_encoder_write_id3tag (SF_PRIVATE *psf)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
unsigned char *id3v2_buffer ;
|
|
int i, id3v2_size ;
|
|
|
|
if (psf->have_written)
|
|
return 0 ;
|
|
|
|
if ((i = mpeg_l3_encoder_construct (psf)))
|
|
return i ;
|
|
|
|
if (psf_fseek (psf, 0, SEEK_SET) != 0)
|
|
return SFE_NOT_SEEKABLE ;
|
|
|
|
/* Safe to call multiple times. */
|
|
id3tag_init (pmpeg->lamef) ;
|
|
|
|
for (i = 0 ; i < SF_MAX_STRINGS ; i++)
|
|
{ switch (psf->strings.data [i].type)
|
|
{ case SF_STR_TITLE :
|
|
id3tag_set_title (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_ARTIST :
|
|
id3tag_set_artist (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_ALBUM :
|
|
id3tag_set_album (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_DATE :
|
|
id3tag_set_year (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_COMMENT :
|
|
id3tag_set_comment (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_GENRE :
|
|
id3tag_set_genre (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
case SF_STR_TRACKNUMBER :
|
|
id3tag_set_track (pmpeg->lamef, psf->strings.storage + psf->strings.data [i].offset) ;
|
|
break ;
|
|
|
|
default:
|
|
break ;
|
|
} ;
|
|
} ;
|
|
|
|
/* The header in this case is the ID3v2 tag header. */
|
|
id3v2_size = lame_get_id3v2_tag (pmpeg->lamef, 0, 0) ;
|
|
if (id3v2_size > 0)
|
|
{ psf_log_printf (psf, "Writing ID3v2 header.\n") ;
|
|
if (! (id3v2_buffer = malloc (id3v2_size)))
|
|
return SFE_MALLOC_FAILED ;
|
|
lame_get_id3v2_tag (pmpeg->lamef, id3v2_buffer, id3v2_size) ;
|
|
psf_fwrite (id3v2_buffer, 1, id3v2_size, psf) ;
|
|
psf->dataoffset = id3v2_size ;
|
|
free (id3v2_buffer) ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
}
|
|
|
|
int
|
|
mpeg_l3_encoder_set_quality (SF_PRIVATE *psf, double compression)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
int bitrate_mode ;
|
|
int bitrate ;
|
|
int ret ;
|
|
|
|
if (compression < 0.0 || compression > 1.0)
|
|
return SF_FALSE ;
|
|
|
|
/*
|
|
** Save the compression setting, as we may have to re-interpret it if
|
|
** the bitrate mode changes.
|
|
*/
|
|
pmpeg->compression = compression ;
|
|
|
|
bitrate_mode = mpeg_l3_encoder_get_bitrate_mode (psf) ;
|
|
if (bitrate_mode == SF_BITRATE_MODE_VARIABLE)
|
|
{ ret = lame_set_VBR_quality (pmpeg->lamef, compression * 10.0) ;
|
|
}
|
|
else
|
|
{ /* Choose a bitrate. */
|
|
if (psf->sf.samplerate >= 32000)
|
|
{ /* MPEG-1.0, bitrates are [32,320] kbps */
|
|
bitrate = (320.0 - (compression * (320.0 - 32.0))) ;
|
|
}
|
|
else if (psf->sf.samplerate >= 16000)
|
|
{ /* MPEG-2.0, bitrates are [8,160] */
|
|
bitrate = (160.0 - (compression * (160.0 - 8.0))) ;
|
|
}
|
|
else
|
|
{ /* MPEG-2.5, bitrates are [8,64] */
|
|
bitrate = (64.0 - (compression * (64.0 - 8.0))) ;
|
|
}
|
|
|
|
if (bitrate_mode == SF_BITRATE_MODE_AVERAGE)
|
|
ret = lame_set_VBR_mean_bitrate_kbps (pmpeg->lamef, bitrate) ;
|
|
else
|
|
ret = lame_set_brate (pmpeg->lamef, bitrate) ;
|
|
} ;
|
|
|
|
if (ret == LAME_OKAY)
|
|
return SF_TRUE ;
|
|
|
|
psf_log_printf (psf, "Failed to set lame encoder quality.\n") ;
|
|
return SF_FALSE ;
|
|
} /* mpeg_l3_encoder_set_quality */
|
|
|
|
int
|
|
mpeg_l3_encoder_set_bitrate_mode (SF_PRIVATE *psf, int mode)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
enum vbr_mode_e vbr_mode ;
|
|
|
|
if (pmpeg->initialized)
|
|
{ psf->error = SFE_CMD_HAS_DATA ;
|
|
return SF_FALSE ;
|
|
} ;
|
|
|
|
switch (mode)
|
|
{ case SF_BITRATE_MODE_CONSTANT : vbr_mode = vbr_off ; break ;
|
|
case SF_BITRATE_MODE_AVERAGE : vbr_mode = vbr_abr ; break ;
|
|
case SF_BITRATE_MODE_VARIABLE : vbr_mode = vbr_default ; break ;
|
|
default :
|
|
psf->error = SFE_BAD_COMMAND_PARAM ;
|
|
return SF_FALSE ;
|
|
} ;
|
|
|
|
if (lame_set_VBR (pmpeg->lamef, vbr_mode) == LAME_OKAY)
|
|
{ /* Re-evaluate the compression setting. */
|
|
return mpeg_l3_encoder_set_quality (psf, pmpeg->compression) ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "Failed to set LAME vbr mode to %d.\n", vbr_mode) ;
|
|
return SF_FALSE ;
|
|
} /* mpeg_l3_encoder_set_bitrate_mode */
|
|
|
|
int
|
|
mpeg_l3_encoder_get_bitrate_mode (SF_PRIVATE *psf)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
enum vbr_mode_e vbr_mode ;
|
|
|
|
vbr_mode = lame_get_VBR (pmpeg->lamef) ;
|
|
|
|
if (vbr_mode == vbr_off)
|
|
return SF_BITRATE_MODE_CONSTANT ;
|
|
if (vbr_mode == vbr_abr)
|
|
return SF_BITRATE_MODE_AVERAGE ;
|
|
if (vbr_mode == vbr_default || vbr_mode < vbr_max_indicator)
|
|
return SF_BITRATE_MODE_VARIABLE ;
|
|
|
|
/* Something is wrong. */
|
|
psf->error = SFE_INTERNAL ;
|
|
return -1 ;
|
|
} /* mpeg_l3_encoder_get_bitrate_mode */
|
|
|
|
|
|
/*-----------------------------------------------------------------------------------------------
|
|
** Private functions.
|
|
*/
|
|
|
|
static int
|
|
mpeg_l3_encoder_close (SF_PRIVATE *psf)
|
|
{ MPEG_L3_ENC_PRIVATE* pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
int ret, len ;
|
|
sf_count_t pos ;
|
|
unsigned char *buffer ;
|
|
|
|
/* Magic number 7200 comes from a comment in lame.h */
|
|
len = 7200 ;
|
|
if (! (buffer = malloc (len)))
|
|
return SFE_MALLOC_FAILED ;
|
|
ret = lame_encode_flush (pmpeg->lamef, buffer, len) ;
|
|
if (ret > 0)
|
|
psf_fwrite (buffer, 1, ret, psf) ;
|
|
|
|
/*
|
|
** Write an IDv1 trailer. The whole tag structure is always 128 bytes, so is
|
|
** guaranteed to fit in the buffer allocated above.
|
|
*/
|
|
ret = lame_get_id3v1_tag (pmpeg->lamef, buffer, len) ;
|
|
if (ret > 0)
|
|
{ psf_log_printf (psf, " Writing ID3v1 trailer.\n") ;
|
|
psf_fwrite (buffer, 1, ret, psf) ;
|
|
} ;
|
|
|
|
/*
|
|
** If possible, seek back and write the LAME/XING/Info headers. This
|
|
** contains information about the whole file and a seek table, and can
|
|
** only be written after encoding.
|
|
**
|
|
** If enabled, Lame wrote an empty header at the beginning of the data
|
|
** that we now fill in.
|
|
*/
|
|
ret = lame_get_lametag_frame (pmpeg->lamef, 0, 0) ;
|
|
if (ret > 0)
|
|
{ if (ret > len)
|
|
{ len = ret ;
|
|
free (buffer) ;
|
|
if (! (buffer = malloc (len)))
|
|
return SFE_MALLOC_FAILED ;
|
|
} ;
|
|
psf_log_printf (psf, " Writing LAME info header at offset %d, %d bytes.\n",
|
|
psf->dataoffset, len) ;
|
|
lame_get_lametag_frame (pmpeg->lamef, buffer, len) ;
|
|
pos = psf_ftell (psf) ;
|
|
if (psf_fseek (psf, psf->dataoffset, SEEK_SET) == psf->dataoffset)
|
|
{ psf_fwrite (buffer, 1, ret, psf) ;
|
|
psf_fseek (psf, pos, SEEK_SET) ;
|
|
} ;
|
|
} ;
|
|
free (buffer) ;
|
|
|
|
free (pmpeg->block) ;
|
|
pmpeg->block = NULL ;
|
|
|
|
if (pmpeg->lamef)
|
|
{ lame_close (pmpeg->lamef) ;
|
|
pmpeg->lamef = NULL ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* mpeg_l3_encoder_close */
|
|
|
|
static void
|
|
mpeg_l3_encoder_log_config (SF_PRIVATE *psf, lame_t lamef)
|
|
{ const char *version ;
|
|
const char *chn_mode ;
|
|
|
|
switch (lame_get_version (lamef))
|
|
{ case 0 : version = "2" ; break ;
|
|
case 1 : version = "1" ; break ;
|
|
case 2 : version = "2.5" ; break ;
|
|
default : version = "unknown!?" ; break ;
|
|
} ;
|
|
switch (lame_get_mode (lamef))
|
|
{ case STEREO : chn_mode = "stereo" ; break ;
|
|
case JOINT_STEREO : chn_mode = "joint-stereo" ; break ;
|
|
case MONO : chn_mode = "mono" ; break ;
|
|
default : chn_mode = "unknown!?" ; break ;
|
|
} ;
|
|
psf_log_printf (psf, " MPEG Version : %s\n", version) ;
|
|
psf_log_printf (psf, " Block samples : %d\n", lame_get_framesize (lamef)) ;
|
|
psf_log_printf (psf, " Channel mode : %s\n", chn_mode) ;
|
|
psf_log_printf (psf, " Samplerate : %d\n", lame_get_out_samplerate (lamef)) ;
|
|
psf_log_printf (psf, " Encoder mode : ") ;
|
|
switch (lame_get_VBR (lamef))
|
|
{ case vbr_off :
|
|
psf_log_printf (psf, "CBR\n") ;
|
|
psf_log_printf (psf, " Bitrate : %d kbps\n", lame_get_brate (lamef)) ;
|
|
break ;
|
|
case vbr_abr :
|
|
psf_log_printf (psf, "ABR\n") ;
|
|
psf_log_printf (psf, " Mean Bitrate : %d kbps\n", lame_get_VBR_mean_bitrate_kbps (lamef)) ;
|
|
break ;
|
|
|
|
case vbr_mt :
|
|
case vbr_default :
|
|
psf_log_printf (psf, "VBR\n") ;
|
|
psf_log_printf (psf, " Quality : %d\n", lame_get_VBR_q (lamef)) ;
|
|
break ;
|
|
|
|
default:
|
|
psf_log_printf (psf, "Unknown!? (%d)\n", lame_get_VBR (lamef)) ;
|
|
break ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, " Encoder delay : %d\n", lame_get_encoder_delay (lamef)) ;
|
|
psf_log_printf (psf, " Write INFO header : %d\n", lame_get_bWriteVbrTag (lamef)) ;
|
|
} /* mpeg_l3_encoder_log_config */
|
|
|
|
static int
|
|
mpeg_l3_encoder_construct (SF_PRIVATE *psf)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
int frame_samples_per_channel ;
|
|
|
|
if (pmpeg->initialized == SF_FALSE)
|
|
{ if (lame_init_params (pmpeg->lamef) < 0)
|
|
{ psf_log_printf (psf, "Failed to initialize lame encoder!\n") ;
|
|
return SFE_INTERNAL ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "Initialized LAME encoder.\n") ;
|
|
mpeg_l3_encoder_log_config (psf, pmpeg->lamef) ;
|
|
|
|
frame_samples_per_channel = lame_get_framesize (pmpeg->lamef) ;
|
|
|
|
/*
|
|
* Suggested output buffer size in bytes from lame.h comment is
|
|
* 1.25 * samples + 7200
|
|
*/
|
|
pmpeg->block_len = (frame_samples_per_channel * 4) / 3 + 7200 ;
|
|
pmpeg->frame_samples = frame_samples_per_channel * psf->sf.channels ;
|
|
|
|
pmpeg->block = malloc (pmpeg->block_len) ;
|
|
if (!pmpeg->block)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
pmpeg->initialized = SF_TRUE ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* mpeg_l3_encoder_construct */
|
|
|
|
static int
|
|
mpeg_l3_encoder_byterate (SF_PRIVATE *psf)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE *) psf->codec_data ;
|
|
int bitrate_mode ;
|
|
int byterate ;
|
|
float calculated_byterate ;
|
|
|
|
bitrate_mode = mpeg_l3_encoder_get_bitrate_mode (psf) ;
|
|
byterate = (lame_get_brate (pmpeg->lamef) + 7) / 8 ;
|
|
|
|
if (bitrate_mode == SF_BITRATE_MODE_VARIABLE)
|
|
{ /*
|
|
** For VBR, lame_get_brate returns the minimum bitrate, so calculate the
|
|
** average byterate so far.
|
|
*/
|
|
calculated_byterate = psf_ftell (psf) - psf->dataoffset ;
|
|
calculated_byterate /= (float) psf->write_current ;
|
|
calculated_byterate *= (float) psf->sf.samplerate ;
|
|
|
|
return SF_MIN (byterate, (int) calculated_byterate) ;
|
|
}
|
|
|
|
return byterate ;
|
|
} /* mpeg_l3_encoder_byterate */
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_short_mono (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
while (len)
|
|
{ writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
|
|
|
|
nbytes = lame_encode_buffer (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_short_stereo (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.sbuf), pmpeg->frame_samples) ;
|
|
while (len)
|
|
{ writecount = SF_MIN (len, max_samples) ;
|
|
/*
|
|
* An oversight, but lame_encode_buffer_interleaved() lacks a const.
|
|
* As such, need another memcpy to not cause a warning.
|
|
*/
|
|
memcpy (ubuf.sbuf, ptr + total, writecount) ;
|
|
nbytes = lame_encode_buffer_interleaved (pmpeg->lamef, ubuf.sbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_int_mono (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
while (len)
|
|
{ writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
|
|
|
|
nbytes = lame_encode_buffer_int (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_int_stereo (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
while (len)
|
|
{ writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
|
|
|
|
nbytes = lame_encode_buffer_interleaved_int (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_float_mono (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
|
|
{ MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
while (len)
|
|
{ writecount = SF_MIN (len, (sf_count_t) pmpeg->frame_samples) ;
|
|
|
|
if (psf->norm_float)
|
|
nbytes = lame_encode_buffer_ieee_float (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
else
|
|
nbytes = lame_encode_buffer_float (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static inline void
|
|
normalize_float (float *dest, const float *src, sf_count_t count, float norm_fact)
|
|
{ while (--count >= 0)
|
|
{ dest [count] = src [count] * norm_fact ;
|
|
} ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_float_stereo (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.fbuf), pmpeg->frame_samples) ;
|
|
while (len)
|
|
{ writecount = SF_MIN (len, max_samples) ;
|
|
|
|
if (psf->norm_float)
|
|
nbytes = lame_encode_buffer_interleaved_ieee_float (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
else
|
|
{ /* Lame lacks a non-normalized interleaved float write. Bummer. */
|
|
normalize_float (ubuf.fbuf, ptr + total, writecount, 1.0 / (float) 0x8000) ;
|
|
nbytes = lame_encode_buffer_interleaved_ieee_float (pmpeg->lamef, ubuf.fbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
}
|
|
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static inline void
|
|
normalize_double (double *dest, const double *src, sf_count_t count, double norm_fact)
|
|
{ while (--count >= 0)
|
|
{ dest [count] = src [count] * norm_fact ;
|
|
} ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_double_mono (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.dbuf), pmpeg->frame_samples) ;
|
|
while (len)
|
|
{ writecount = SF_MIN (len, max_samples) ;
|
|
|
|
if (psf->norm_double)
|
|
nbytes = lame_encode_buffer_ieee_double (pmpeg->lamef, ptr + total, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
else
|
|
{ /* Lame lacks non-normalized double writing */
|
|
normalize_double (ubuf.dbuf, ptr + total, writecount, 1.0 / (double) 0x8000) ;
|
|
nbytes = lame_encode_buffer_ieee_double (pmpeg->lamef, ubuf.dbuf, NULL, writecount, pmpeg->block, pmpeg->block_len) ;
|
|
}
|
|
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
|
|
static sf_count_t
|
|
mpeg_l3_encode_write_double_stereo (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
|
|
{ BUF_UNION ubuf ;
|
|
MPEG_L3_ENC_PRIVATE *pmpeg = (MPEG_L3_ENC_PRIVATE*) psf->codec_data ;
|
|
sf_count_t total = 0 ;
|
|
int nbytes, writecount, writen ;
|
|
|
|
if ((psf->error = mpeg_l3_encoder_construct (psf)))
|
|
return 0 ;
|
|
|
|
const sf_count_t max_samples = SF_MIN (ARRAY_LEN (ubuf.dbuf), pmpeg->frame_samples) ;
|
|
while (len)
|
|
{ writecount = SF_MIN (len, max_samples) ;
|
|
|
|
if (psf->norm_double)
|
|
nbytes = lame_encode_buffer_interleaved_ieee_double (pmpeg->lamef, ptr + total, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
else
|
|
{ /* Lame lacks interleaved non-normalized double writing */
|
|
normalize_double (ubuf.dbuf, ptr + total, writecount, 1.0 / (double) 0x8000) ;
|
|
nbytes = lame_encode_buffer_interleaved_ieee_double (pmpeg->lamef, ubuf.dbuf, writecount / 2, pmpeg->block, pmpeg->block_len) ;
|
|
}
|
|
|
|
if (nbytes < 0)
|
|
{ psf_log_printf (psf, "lame_encode_buffer returned %d\n", nbytes) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (nbytes)
|
|
{ writen = psf_fwrite (pmpeg->block, 1, nbytes, psf) ;
|
|
if (writen != nbytes)
|
|
{ psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", writen, nbytes) ;
|
|
} ;
|
|
} ;
|
|
|
|
total += writecount ;
|
|
len -= writecount ;
|
|
} ;
|
|
|
|
return total ;
|
|
}
|
|
|
|
#else /* HAVE_MPEG */
|
|
|
|
int
|
|
mpeg_l3_encoder_init (SF_PRIVATE *psf, int UNUSED (vbr))
|
|
{ psf_log_printf (psf, "This version of libsndfile was compiled without MPEG Layer 3 encoding support.\n") ;
|
|
return SFE_UNIMPLEMENTED ;
|
|
} /* mpeg_l3_encoder_init */
|
|
|
|
#endif
|