Audio-MPEG

 view release on metacpan or  search on metacpan

MPEG.xs  view on Meta::CPAN


#
# 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++;

MPEG.xs  view on Meta::CPAN

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];
			}

MPEG.xs  view on Meta::CPAN

			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 )