| /* | |
| ** 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 |