/* * * This file is part of libmpeg3 * * LibMPEG3 * Author: Adam Williams * Page: heroine.linuxbox.com * Page: http://www.smalltalkconsulting.com/html/mpeg3source.html (for Squeak) * LibMPEG3 was originally licenced under GPL. It was relicensed by the author under the LGPL and the Squeak license on Nov 1st, 2000 This library 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 library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also licensed under the Squeak license. http://www.squeak.org/license.html */ /* Changed Sept 15th by John M McIntosh to support Macintosh & Squeak */ #include "libmpeg3.h" #include "mpeg3private.h" #include "mpeg3protos.h" #include "mpeg3audio.h" #include "tables.h" #include #include mpeg3audio_t* mpeg3audio_allocate_struct(mpeg3_t *file, mpeg3_atrack_t *track) { mpeg3audio_t *audio = (mpeg3audio_t *) memoryAllocate(1, sizeof(mpeg3audio_t)); audio->file = file; audio->track = track; audio->astream = mpeg3bits_new_stream(file, track->demuxer); audio->outscale = 1; audio->bsbuf = audio->bsspace[1]; audio->init = 1; audio->bo = 1; audio->channels = 1; return audio; } int mpeg3audio_delete_struct(mpeg3audio_t *audio) { mpeg3bits_delete_stream(audio->astream); if(audio->pcm_sample) memoryFree(audio->pcm_sample); memoryFree(audio); return 0; } int mpeg3audio_replace_buffer(mpeg3audio_t *audio, long new_allocation) { long i; if(!audio->pcm_sample) { audio->pcm_sample = (float *) memoryAllocate(1,sizeof(float) * new_allocation * audio->channels); audio->pcm_allocated = new_allocation; } else { float *new_samples = (float *) memoryAllocate(1,sizeof(float) * new_allocation * audio->channels); for(i = 0; i < audio->pcm_allocated * audio->channels; i++) { new_samples[i] = audio->pcm_sample[i]; } memoryFree(audio->pcm_sample); audio->pcm_sample = new_samples; audio->pcm_allocated = new_allocation; } return 0; } int mpeg3audio_read_frame(mpeg3audio_t *audio) { int result = 0; result = mpeg3audio_read_header(audio); if(!result) { switch(audio->format) { case AUDIO_AC3: //JMM result = mpeg3audio_do_ac3(audio); result = -1; //JMM break; case AUDIO_MPEG: switch(audio->layer) { case 1: break; case 2: result = mpeg3audio_dolayer2(audio); break; case 3: result = mpeg3audio_dolayer3(audio); break; default: result = 1; break; } break; case AUDIO_PCM: result = mpeg3audio_do_pcm(audio); break; } } if(!result) { /* Byte align the stream */ mpeg3bits_byte_align(audio->astream); } return result; } /* Get the length but also initialize the frame sizes. */ int mpeg3audio_get_length(mpeg3audio_t *audio, mpeg3_atrack_t *track) { long result = 0; long framesize1 = 0, total1 = 0; long framesize2 = 0, total2 = 0; long total_framesize = 0, total_frames = 0; long byte_limit = 131072; /* Total bytes to gather information from */ long total_bytes = 0; long major_framesize; /* Bigger framesize + header */ long minor_framesize; /* Smaller framesize + header */ long major_total; long minor_total; mpeg3_t *file = (mpeg3_t *) audio->file; /* Get the frame sizes */ mpeg3bits_seek_start(audio->astream); audio->pcm_point = 0; result = mpeg3audio_read_frame(audio); /* Stores the framesize */ audio->samples_per_frame = audio->pcm_point / audio->channels; switch(audio->format) { case AUDIO_AC3: audio->avg_framesize = audio->framesize; break; case AUDIO_MPEG: framesize1 = audio->framesize; total_bytes += audio->framesize; total1 = 1; while(!result && total_bytes < byte_limit) { audio->pcm_point = 0; result = mpeg3audio_read_frame(audio); total_bytes += audio->framesize; if(audio->framesize != framesize1) { framesize2 = audio->framesize; total2 = 1; break; } else { total1++; } } while(!result && total_bytes < byte_limit) { audio->pcm_point = 0; result = mpeg3audio_read_frame(audio); total_bytes += audio->framesize; if(audio->framesize != framesize2) { break; } else { total2++; } } audio->pcm_point = 0; result = mpeg3audio_read_frame(audio); if(audio->framesize != framesize1 && audio->framesize != framesize2) { /* Variable bit rate. Get the average frame size. */ while(!result && total_bytes < byte_limit) { audio->pcm_point = 0; result = mpeg3audio_read_frame(audio); total_bytes += audio->framesize; if(!result) { total_framesize += audio->framesize; total_frames++; } } audio->avg_framesize = 4 + (float)(total_framesize + framesize1 + framesize2) / (total_frames + total1 + total2); } else { major_framesize = framesize2 > framesize1 ? framesize2 : framesize1; major_total = framesize2 > framesize1 ? total2 : total1; minor_framesize = framesize2 > framesize1 ? framesize1 : framesize2; minor_total = framesize2 > framesize1 ? total1 : total2; /* Add the headers to the framesizes */ audio->avg_framesize = 4 + (float)(major_framesize * major_total + minor_framesize * minor_total) / (major_total + minor_total); } break; case AUDIO_PCM: break; } /* Estimate the total samples */ if(file->is_audio_stream) { /* From the raw file */ result = (long)((float)mpeg3demuxer_total_bytes(audio->astream->demuxer) / audio->avg_framesize * audio->samples_per_frame); } else { /* Gross approximation from a multiplexed file. */ result = (long)(mpeg3demux_length(audio->astream->demuxer) * track->sample_rate); /* result = (long)((float)mpeg3_video_frames(file, 0) / mpeg3_frame_rate(file, 0) * track->sample_rate); */ /* We would scan the multiplexed packets here for the right timecode if only */ /* they had meaningful timecode. */ } audio->pcm_point = 0; mpeg3bits_seek_start(audio->astream); mpeg3audio_reset_synths(audio); return result; } int mpeg3audio_seek(mpeg3audio_t *audio, long position) { int result = 0; mpeg3_t *file = (mpeg3_t *) audio->file; mpeg3_atrack_t *track = (mpeg3_atrack_t *) audio->track; long frame_number; long byte_position = 0; double time_position; /* Sample seek wasn't requested */ if(audio->sample_seek < 0) { audio->pcm_position = position; audio->pcm_size = 0; return 0; } /* Can't slide buffer. Seek instead. */ if(!file->is_audio_stream) { /* Seek in a multiplexed stream using the multiplexer. */ time_position = (double)position / track->sample_rate; result |= mpeg3bits_seek_time(audio->astream, time_position); audio->pcm_position = mpeg3bits_packet_time(audio->astream) * track->sample_rate; /*printf("wanted %f got %f\n", time_position, mpeg3bits_packet_time(audio->astream)); */ } else { /* Seek in an elemental stream. This algorithm achieves sample accuracy on fixed bitrates. */ /* Forget about variable bitrates or program streams. */ frame_number = position / audio->samples_per_frame; byte_position = (long)(audio->avg_framesize * frame_number); audio->pcm_position = frame_number * audio->samples_per_frame; if(byte_position < audio->avg_framesize * 2) { result |= mpeg3bits_seek_start(audio->astream); audio->pcm_position = 0; } else { result |= mpeg3bits_seek_byte(audio->astream, byte_position); } } /* Arm the backstep buffer for layer 3 if not at the beginning already. */ if(byte_position >= audio->avg_framesize * 2 && audio->layer == 3 && !result) { result |= mpeg3audio_prev_header(audio); result |= mpeg3audio_read_layer3_frame(audio); } /* Reset the tables. */ mpeg3audio_reset_synths(audio); audio->pcm_size = 0; audio->pcm_point = 0; return result; } /* ================================================================ */ /* ENTRY POINTS */ /* ================================================================ */ mpeg3audio_t* mpeg3audio_new(mpeg3_t *file, mpeg3_atrack_t *track, int format) { mpeg3audio_t *audio = mpeg3audio_allocate_struct(file, track); int result = 0; /* Init tables */ mpeg3audio_new_decode_tables(audio); audio->percentage_seek = -1; audio->sample_seek = -1; audio->format = format; /* Determine the format of the stream */ if(format == AUDIO_UNKNOWN) { if(((mpeg3bits_showbits(audio->astream, 32) & 0xffff0000) >> 16) == MPEG3_AC3_START_CODE) audio->format = AUDIO_AC3; else audio->format = AUDIO_MPEG; } /* get channel count */ result = mpeg3audio_read_header(audio); /* Set up the sample buffer */ mpeg3audio_replace_buffer(audio, 262144); /* Copy information to the mpeg struct */ if(!result) { track->channels = audio->channels; switch(audio->format) { case AUDIO_AC3: //JMM track->sample_rate = mpeg3_ac3_samplerates[audio->sampling_frequency_code]; break; case AUDIO_MPEG: track->sample_rate = mpeg3_freqs[audio->sampling_frequency_code]; break; case AUDIO_PCM: track->sample_rate = 48000; break; } track->total_samples = mpeg3audio_get_length(audio, track); result |= mpeg3bits_seek_start(audio->astream); } else { mpeg3audio_delete_struct(audio); audio = 0; } return audio; } int mpeg3audio_delete(mpeg3audio_t *audio) { mpeg3audio_delete_struct(audio); return 0; } int mpeg3audio_seek_percentage(mpeg3audio_t *audio, double percentage) { audio->percentage_seek = percentage; return 0; } int mpeg3audio_seek_sample(mpeg3audio_t *audio, long sample) { audio->sample_seek = sample; return 0; } /* Read raw frames for concatenation purposes */ int mpeg3audio_read_raw(mpeg3audio_t *audio, unsigned char *output, long *size, long max_size) { int result = 0; int i; *size = 0; switch(audio->format) { case AUDIO_AC3: /* Just write the AC3 stream */ if(mpeg3bits_read_buffer(audio->astream, output, audio->framesize)) return 1; *size = audio->framesize; break; case AUDIO_MPEG: /* Fix the mpeg stream */ result = mpeg3audio_read_header(audio); if(!result) { if(max_size < 4) return 1; *output++ = (audio->newhead & 0xff000000) >> 24; *output++ = (audio->newhead & 0xff0000) >> 16; *output++ = (audio->newhead & 0xff00) >> 8; *output++ = (audio->newhead & 0xff); *size += 4; if(max_size < 4 + audio->framesize) return 1; if(mpeg3bits_read_buffer(audio->astream, output, audio->framesize)) return 1; *size += audio->framesize; } break; case AUDIO_PCM: if(mpeg3bits_read_buffer(audio->astream, output, audio->framesize)) return 1; *size = audio->framesize; break; } return result; } /* Channel is 0 to channels - 1 */ int mpeg3audio_decode_audio(mpeg3audio_t *audio, float *output_f, short *output_i, int channel, long start_position, long len) { long allocation_needed = len + MPEG3AUDIO_PADDING; long i, j, result = 0; mpeg3_t *file = (mpeg3_t *) audio->file; mpeg3_atrack_t *atrack = (mpeg3_atrack_t *) audio->track; long attempts; /* Create new buffer */ if(audio->pcm_allocated < allocation_needed) { mpeg3audio_replace_buffer(audio, allocation_needed); } /* There was a percentage seek */ if(audio->percentage_seek >= 0) { mpeg3bits_seek_percentage(audio->astream, audio->percentage_seek); /* Force the pcm buffer to be reread. */ audio->pcm_position = start_position; audio->pcm_size = 0; audio->percentage_seek = -1; } else { /* Entire output is in buffer so don't do anything. */ if(start_position >= audio->pcm_position && start_position < audio->pcm_position + audio->pcm_size && start_position + len <= audio->pcm_size) { ; } else /* Output starts in buffer but ends later so slide it back. */ if(start_position <= audio->pcm_position + audio->pcm_size && start_position >= audio->pcm_position) { for(i = 0, j = (start_position - audio->pcm_position) * audio->channels; j < audio->pcm_size * audio->channels; i++, j++) { audio->pcm_sample[i] = audio->pcm_sample[j]; } audio->pcm_point = i; audio->pcm_size -= start_position - audio->pcm_position; audio->pcm_position = start_position; } else { /* Output is outside buffer completely. */ result = mpeg3audio_seek(audio, start_position); audio->sample_seek = -1; /* Check sanity */ if(start_position < audio->pcm_position) audio->pcm_position = start_position; } audio->sample_seek = -1; } /* Read packets until the buffer is full. */ if(!result) { attempts = 0; while(attempts < 6 && !mpeg3bits_eof(audio->astream) && audio->pcm_size + audio->pcm_position < start_position + len) { result = mpeg3audio_read_frame(audio); if(result) attempts++; audio->pcm_size = audio->pcm_point / audio->channels; } } /* Copy the buffer to the output */ if(output_f) { for(i = 0, j = (start_position - audio->pcm_position) * audio->channels + channel; i < len && j < audio->pcm_size * audio->channels; i++, j += audio->channels) { output_f[i] = audio->pcm_sample[j]; } for( ; i < len; i++) { output_f[i] = 0; } } else if(output_i) { int sample; for(i = 0, j = (start_position - audio->pcm_position) * audio->channels + channel; i < len && j < audio->pcm_size * audio->channels; i++, j += audio->channels) { sample = (int)(audio->pcm_sample[j] * 32767); if(sample > 32767) sample = 32767; else if(sample < -32768) sample = -32768; output_i[i] = sample; } for( ; i < len; i++) { output_i[i] = 0; } } if(audio->pcm_point > 0) return 0; else return result; }