Audio-MPEG
view release on metacpan or search on metacpan
#
# MAD version strings
#
SV *
version(THIS)
Audio_MPEG_Decode THIS
PREINIT:
HV *h;
char *key;
CODE:
h = (HV *)sv_2mortal((SV *)newHV());
key = "version";
hv_store(h, key, strlen(key), newSVpv((char *)mad_version, 0), 0);
key = "copyright";
hv_store(h, key, strlen(key), newSVpv((char *)mad_copyright, 0), 0);
key = "author";
hv_store(h, key, strlen(key), newSVpv((char *)mad_author, 0), 0);
key = "build";
hv_store(h, key, strlen(key), newSVpv((char *)mad_build, 0), 0);
RETVAL = newRV((SV *)h);
OUTPUT:
RETVAL
#
# Add chunk of MP3 data to decoding buffer.
#
int
buffer(THIS, data)
Audio_MPEG_Decode THIS
SV *data
PREINIT:
unsigned char *buf;
size_t len;
CODE:
buf = SvPV(data, len);
RETVAL = decode_buffer(THIS, buf, len); // in decode.c
OUTPUT:
RETVAL
#
# Decode MP3 frame(s) that have been added to the buffer. Can also
# be used to simply verify the frame headers by passing TRUE as a flag
# (this is useful for quickly verifying the integrity of the stream)
#
void
decode_frame(THIS, header_only = 0)
Audio_MPEG_Decode THIS
int header_only
PREINIT:
struct mad_stream *stream;
struct mad_frame *frame;
struct mad_header *header;
int err = 0;
unsigned long tagsize;
PPCODE:
stream = THIS->stream;
frame = THIS->frame;
header = &frame->header;
decode_loop:
if (mad_header_decode(header, stream) == -1) {
switch (stream->error) {
case MAD_ERROR_BUFLEN:
XSRETURN_NO;
break;
case MAD_ERROR_LOSTSYNC:
if (strncmp(stream->this_frame, "TAG", 3) == 0) {
mad_stream_skip(stream, 128);
goto decode_loop;
} else if (strncmp(stream->this_frame, "ID3", 3) == 0) {
stream->error = 0x0666;
tagsize = (((stream->this_frame[6] & 0x7f) << 21)
| ((stream->this_frame[7] & 0x7f) << 14)
| ((stream->this_frame[8] & 0x7f) << 7)
| ((stream->this_frame[9] & 0x7f))) ; //+ 10;
if (tagsize > stream->bufend - stream->this_frame) {
mad_stream_skip(stream, stream->bufend -
stream->this_frame);
} else {
mad_stream_skip(stream, tagsize);
}
goto decode_loop;
}
/* fall through case */
default:
err++;
break;
}
}
if (! header_only) {
if (mad_frame_decode(frame, stream) == -1) {
switch (stream->error) {
case MAD_ERROR_BUFLEN:
XSRETURN_NO;
break;
case MAD_ERROR_LOSTSYNC:
if (strncmp(stream->this_frame, "TAG", 3) == 0) {
mad_stream_skip(stream, 128);
goto decode_loop;
} else if (strncmp(stream->this_frame, "ID3", 3) == 0) {
stream->error = 0x0666;
tagsize = (((stream->this_frame[6] & 0x7f) << 21)
| ((stream->this_frame[7] & 0x7f) << 14)
| ((stream->this_frame[8] & 0x7f) << 7)
| ((stream->this_frame[9] & 0x7f))) ; //+ 10;
if (tagsize > stream->bufend - stream->this_frame) {
mad_stream_skip(stream, stream->bufend -
stream->this_frame);
} else {
mad_stream_skip(stream, tagsize);
}
goto decode_loop;
}
/* fall through case */
default:
err++;
MODULE = Audio::MPEG PACKAGE = Audio::MPEG::Output
Audio_MPEG_Output
new(CLASS, params_data_ref = &PL_sv_undef)
char *CLASS = NO_INIT
SV *params_data_ref
PREINIT:
HV *params_data;
SV **hval;
char *key;
CODE:
Newz(0, (void *)RETVAL, sizeof(*RETVAL), char);
output_new(RETVAL);
/* set up defaults */
RETVAL->params->samplerate = 44100;
RETVAL->params->channels = 2;
RETVAL->params->mode = AUDIO_MODE_DITHER;
RETVAL->params->type = AUDIO_MPEG_OUTPUT_TYPE_FLOAT;
/* process input arguments */
if (items > 1) {
params_data = (HV *)SvRV(params_data_ref);
key = "out_sample_rate";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->params->samplerate = SvUV(*hval);
key = "out_channels";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->params->channels = SvUV(*hval);
key = "mode";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->params->mode = SvUV(*hval);
key = "type";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->params->type = SvUV(*hval);
key = "apply_delay";
RETVAL->decode_delay_applied = 1;
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->decode_delay_applied = SvUV(*hval) ? 0 : 1;
}
OUTPUT:
RETVAL
void
DESTROY(THIS)
Audio_MPEG_Output THIS
CODE:
output_DESTROY(THIS);
Safefree(THIS);
#
# Some audio formats require a header. This will generate one.
#
void
header(THIS, datasize = 0)
Audio_MPEG_Output THIS
unsigned int datasize
PREINIT:
struct audio_params *params;
PPCODE:
params = THIS->params;
switch (params->type) {
case AUDIO_MPEG_OUTPUT_TYPE_SND:
{
unsigned char header[24];
if (!datasize)
datasize = ~0; /* (unsigned) -1 */
audio_snd_header(params, datasize, header, sizeof(header));
XPUSHs(sv_2mortal(newSVpvn(header, sizeof(header))));
}
break;
case AUDIO_MPEG_OUTPUT_TYPE_WAVE:
{
unsigned char header[44];
audio_wave_header(params, datasize, header, sizeof(header));
XPUSHs(sv_2mortal(newSVpvn(header, sizeof(header))));
}
break;
default:
XPUSHs(sv_2mortal(newSVpv("", 0)));
break;
}
#
# Transform mad_fixed_t PCM stream to a more usable stream.
#
void
encode(THIS, pcm)
Audio_MPEG_Output THIS
struct mad_pcm *pcm
PREINIT:
mad_fixed_t const *left = NULL;
mad_fixed_t const *right = NULL;
unsigned int pcm_len;
unsigned int delay = 0;
struct audio_params *params;
struct audio_stats *stats;
struct audio_dither_err *dither_err;
unsigned int len;
unsigned char data[MAX_NSAMPLES * sizeof(double) * 2];
mad_fixed_t mono[MAX_NSAMPLES];
PPCODE:
params = THIS->params;
stats = THIS->stats;
dither_err = THIS->dither_err;
if (!pcm->length) {
warn("pcm sample length cannot be 0");
XSRETURN_UNDEF;
}
if (! THIS->decode_delay_applied) {
THIS->decode_delay_applied = 1;
delay = pcm->length / 2;
}
if (!pcm->channels || pcm->channels > 2) {
warn("number of pcm channels must be either 1 or 2");
XSRETURN_UNDEF;
}
if (!pcm->samplerate) {
warn("pcm sample rate cannot be 0");
XSRETURN_UNDEF;
}
if (!THIS->resample_init) {
/* if difference of 6% or more, resample */
if (abs(params->samplerate - pcm->samplerate) >
6L * params->samplerate / 100) {
if (resample_init(&THIS->resample[0],
pcm->samplerate, params->samplerate) == -1 ||
resample_init(&THIS->resample[1],
pcm->samplerate, params->samplerate) == -1) {
warn("cannot resample");
} else {
THIS->do_resample = 1;
}
}
THIS->resample_init = 1;
}
left = pcm->samples[0] + delay;
if (pcm->channels == 2)
right = pcm->samples[1] + delay;
if ((pcm_len = pcm->length - delay) < 1) {
warn("pcm sample length cannot be less than 1");
XSRETURN_UNDEF;
}
if (THIS->do_resample) {
unsigned int old_pcm_len = pcm_len;
pcm_len = resample_block(&THIS->resample[0], old_pcm_len, left,
(*THIS->resampled)[0]);
left = (*THIS->resampled)[0];
if (pcm->channels == 2) {
resample_block(&THIS->resample[1], old_pcm_len, right,
(*THIS->resampled)[1]);
right = (*THIS->resampled)[1];
}
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
flags->lowpassfreq = -1;
key = "highpass_filter_frequency";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
flags->highpassfreq = SvIV(*hval);
key = "highpass_filter_width";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
flags->highpasswidth = SvIV(*hval);
key = "no_highpass_filter";
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
flags->highpassfreq = -1;
key = "apply_delay";
RETVAL->encode_delay_applied = 1;
if ((hval = hv_fetch(params_data, key, strlen(key), FALSE)) != NULL)
RETVAL->encode_delay_applied = SvUV(*hval) ? 0 : 1;
}
/* finish initialization of parameters */
if (lame_init_params(flags) == -1) {
warn("unable to initialize LAME library parameters");
XSRETURN_UNDEF;
}
OUTPUT:
RETVAL
void
DESTROY(THIS)
Audio_MPEG_Encode THIS
CODE:
lame_close(THIS->flags);
Safefree(THIS);
#
# This is the delay, in PCM samples, that is introduced by encoding.
#
int
encoder_delay(THIS)
Audio_MPEG_Encode THIS
CODE:
RETVAL = THIS->flags->encoder_delay;
OUTPUT:
RETVAL
#
# encode_float() - encodes interleaved float pcm samples (maximum precision)
#
void
encode_float(THIS, pcm_sv)
Audio_MPEG_Encode THIS
SV *pcm_sv
PREINIT:
unsigned char *pcm;
STRLEN pcm_len;
unsigned int encode_len;
unsigned char out[LAME_MAXMP3BUFFER / sizeof(short) * sizeof(float)];
PPCODE:
pcm = SvPV(pcm_sv, pcm_len);
if (!pcm_len) {
warn("pcm sample length cannot be 0");
XSRETURN_UNDEF;
}
if (! THIS->encode_delay_applied) {
/* skip n input samples to compensate for encoding delay */
THIS->encode_delay_applied = 1;
pcm += THIS->flags->encoder_delay * sizeof(float) *
THIS->flags->num_channels;
pcm_len -= THIS->flags->encoder_delay;
if ((pcm_len -= THIS->flags->encoder_delay) < 1) {
warn("pcm sample length is less than 0 after encoder delay compensation");
XSRETURN_UNDEF;
}
}
encode_len = lame_encode_buffer_interleaved_float(THIS->flags,
(float *)pcm, pcm_len / sizeof(float) / THIS->flags->num_channels,
out, LAME_MAXMP3BUFFER);
XPUSHs(sv_2mortal(newSVpvn(out, encode_len)));
#
# encode16() - encodes interleaved signed 16bit pcm samples
#
void
encode16(THIS, pcm_sv)
Audio_MPEG_Encode THIS
SV *pcm_sv
PREINIT:
unsigned char *pcm;
STRLEN pcm_len;
unsigned int encode_len;
unsigned char out[LAME_MAXMP3BUFFER];
PPCODE:
pcm = SvPV(pcm_sv, pcm_len);
if (!pcm_len) {
warn("pcm sample length cannot be 0");
XSRETURN_UNDEF;
}
if (! THIS->encode_delay_applied) {
/* skip n input samples to compensate for encoding delay */
THIS->encode_delay_applied = 1;
pcm += THIS->flags->encoder_delay * sizeof(short) *
THIS->flags->num_channels;
if ((pcm_len -= THIS->flags->encoder_delay) < 1) {
warn("pcm sample length is less than 0 after encoder delay compensation");
XSRETURN_UNDEF;
}
}
if (THIS->flags->num_channels == 2) {
encode_len = lame_encode_buffer_interleaved(THIS->flags,
(short *)pcm, pcm_len / sizeof(short) /
THIS->flags->num_channels, out, LAME_MAXMP3BUFFER);
} else {
encode_len = lame_encode_buffer(THIS->flags,
(short *)pcm, (short *)pcm, pcm_len / sizeof(short) /
THIS->flags->num_channels, out, LAME_MAXMP3BUFFER);
}
XPUSHs(sv_2mortal(newSVpvn(out, encode_len)));
#
# This must always be called after one thinks the encoding is finished
#
void
encode_flush(THIS)
Audio_MPEG_Encode THIS
PREINIT:
unsigned int encode_len;
unsigned char out[LAME_MAXMP3BUFFER];
PPCODE:
encode_len = lame_encode_flush(THIS->flags, out, LAME_MAXMP3BUFFER);
XPUSHs(sv_2mortal(newSVpvn(out, encode_len)));
#
# Write the Xing VBR header to an MP3 file. NOTE: file *must* be opened
# read/write!
#
void
encode_vbr_flush(THIS, fp)
Audio_MPEG_Encode THIS
FILE *fp
CODE:
lame_mp3_tags_fid(THIS->flags, fp);
( run in 2.505 seconds using v1.01-cache-2.11-cpan-71847e10f99 )