mirror of
https://github.com/cookiengineer/audacity
synced 2026-02-06 03:32:09 +01:00
304 lines
8.3 KiB
C++
304 lines
8.3 KiB
C++
/*
|
|
mp3tech.c - Functions for handling MP3 files and most MP3 data
|
|
structure manipulation.
|
|
|
|
Copyright (C) 2000-2006 Cedric Tefft <cedric@phreaker.net>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
***************************************************************************
|
|
|
|
This file is based in part on:
|
|
|
|
* MP3Info 0.5 by Ricardo Cerqueira <rmc@rccn.net>
|
|
* MP3Stat 0.9 by Ed Sweetman <safemode@voicenet.com> and
|
|
Johannes Overmann <overmann@iname.com>
|
|
|
|
*/
|
|
|
|
#include "mp3tech.h"
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
namespace _sbsms_ {
|
|
int layer_tab[4]= {0, 3, 2, 1};
|
|
|
|
int frequencies[3][4] = {
|
|
{22050,24000,16000,50000}, /* MPEG 2.0 */
|
|
{44100,48000,32000,50000}, /* MPEG 1.0 */
|
|
{11025,12000,8000,50000} /* MPEG 2.5 */
|
|
};
|
|
|
|
int bitrate[2][3][15] = {
|
|
{ /* MPEG 2.0 */
|
|
{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256}, /* layer 1 */
|
|
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}, /* layer 2 */
|
|
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160} /* layer 3 */
|
|
|
|
},
|
|
|
|
{ /* MPEG 1.0 */
|
|
{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448}, /* layer 1 */
|
|
{0,32,48,56,64,80,96,112,128,160,192,224,256,320,384}, /* layer 2 */
|
|
{0,32,40,48,56,64,80,96,112,128,160,192,224,256,320} /* layer 3 */
|
|
}
|
|
};
|
|
|
|
int frame_samples_index[] = {384,1152,1152};
|
|
|
|
int frame_size_index[] = {24000, 72000, 72000};
|
|
|
|
|
|
char *mode_text[] = {
|
|
"stereo", "joint stereo", "dual channel", "mono"
|
|
};
|
|
|
|
char *emphasis_text[] = {
|
|
"none", "50/15 microsecs", "reserved", "CCITT J 17"
|
|
};
|
|
|
|
|
|
int get_mp3_info(mp3info *mp3)
|
|
{
|
|
int had_error = 0;
|
|
int frame_type[15]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
|
int samples = 0;
|
|
int bitrate;
|
|
int frames=0,frame_types=0,frames_so_far=0;
|
|
int counter=0;
|
|
mp3header header;
|
|
struct stat filestat;
|
|
off_t data_start=0;
|
|
|
|
int scantype = SCAN_FULL;
|
|
|
|
stat(mp3->filename,&filestat);
|
|
mp3->datasize=filestat.st_size;
|
|
get_id3(mp3);
|
|
|
|
if(scantype == SCAN_FULL) {
|
|
if(get_first_header(mp3,0L)) {
|
|
data_start=ftell(mp3->file);
|
|
while((bitrate=get_next_header(mp3))) {
|
|
frame_type[15-bitrate]++;
|
|
frames++;
|
|
}
|
|
memcpy(&header,&(mp3->header),sizeof(mp3header));
|
|
for(counter=0;counter<15;counter++) {
|
|
if(frame_type[counter]) {
|
|
frame_types++;
|
|
header.bitrate=counter;
|
|
frames_so_far += frame_type[counter];
|
|
samples += frame_samples(&mp3->header)*frame_type[counter];
|
|
mp3->sample_rate = header_frequency(&mp3->header);
|
|
}
|
|
}
|
|
mp3->samples = samples - frame_samples_index[2];
|
|
}
|
|
}
|
|
|
|
return had_error;
|
|
}
|
|
|
|
|
|
int get_first_header(mp3info *mp3, long startpos)
|
|
{
|
|
int k, l=0,c;
|
|
mp3header h, h2;
|
|
long valid_start=0;
|
|
|
|
fseek(mp3->file,startpos,SEEK_SET);
|
|
while (1) {
|
|
while((c=fgetc(mp3->file)) != 255 && (c != EOF));
|
|
if(c == 255) {
|
|
ungetc(c,mp3->file);
|
|
valid_start=ftell(mp3->file);
|
|
if((l=get_header(mp3->file,&h))) {
|
|
fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
|
|
for(k=1; (k < MIN_CONSEC_GOOD_FRAMES) && (mp3->datasize-ftell(mp3->file) >= FRAME_HEADER_SIZE); k++) {
|
|
if(!(l=get_header(mp3->file,&h2))) break;
|
|
if(!sameConstant(&h,&h2)) break;
|
|
fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
|
|
}
|
|
if(k == MIN_CONSEC_GOOD_FRAMES) {
|
|
fseek(mp3->file,valid_start,SEEK_SET);
|
|
memcpy(&(mp3->header),&h2,sizeof(mp3header));
|
|
mp3->header_isvalid=1;
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* get_next_header() - read header at current position or look for
|
|
the next valid header if there isn't one at the current position
|
|
*/
|
|
int get_next_header(mp3info *mp3)
|
|
{
|
|
int l=0,c,skip_bytes=0;
|
|
mp3header h;
|
|
|
|
while(1) {
|
|
while((c=fgetc(mp3->file)) != 255 && (ftell(mp3->file) < mp3->datasize)) skip_bytes++;
|
|
if(c == 255) {
|
|
ungetc(c,mp3->file);
|
|
if((l=get_header(mp3->file,&h))) {
|
|
fseek(mp3->file,l-FRAME_HEADER_SIZE,SEEK_CUR);
|
|
return 15-h.bitrate;
|
|
} else {
|
|
skip_bytes += FRAME_HEADER_SIZE;
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Get next MP3 frame header.
|
|
Return codes:
|
|
positive value = Frame Length of this header
|
|
0 = No, we did not retrieve a valid frame header
|
|
*/
|
|
|
|
int get_header(FILE *file,mp3header *header)
|
|
{
|
|
unsigned char buffer[FRAME_HEADER_SIZE];
|
|
int fl;
|
|
|
|
if(fread(&buffer,FRAME_HEADER_SIZE,1,file)<1) {
|
|
header->sync=0;
|
|
return 0;
|
|
}
|
|
header->sync=(((int)buffer[0]<<4) | ((int)(buffer[1]&0xE0)>>4));
|
|
if(buffer[1] & 0x10) header->version=(buffer[1] >> 3) & 1;
|
|
else header->version=2;
|
|
header->layer=(buffer[1] >> 1) & 3;
|
|
header->bitrate=(buffer[2] >> 4) & 0x0F;
|
|
if((header->sync != 0xFFE) || (header->layer != 1) || (header->bitrate == 0xF)) {
|
|
header->sync=0;
|
|
return 0;
|
|
}
|
|
header->crc=buffer[1] & 1;
|
|
header->freq=(buffer[2] >> 2) & 0x3;
|
|
header->padding=(buffer[2] >>1) & 0x1;
|
|
header->extension=(buffer[2]) & 0x1;
|
|
header->mode=(buffer[3] >> 6) & 0x3;
|
|
header->mode_extension=(buffer[3] >> 4) & 0x3;
|
|
header->copyright=(buffer[3] >> 3) & 0x1;
|
|
header->original=(buffer[3] >> 2) & 0x1;
|
|
header->emphasis=(buffer[3]) & 0x3;
|
|
|
|
/* Final sanity checks: bitrate 1111b and frequency 11b are reserved (invalid) */
|
|
if (header->bitrate == 0x0F || header->freq == 0x3) {
|
|
return 0;
|
|
}
|
|
|
|
return ((fl=frame_length(header)) >= MIN_FRAME_SIZE ? fl : 0);
|
|
}
|
|
|
|
int frame_samples(mp3header *header) {
|
|
return frame_samples_index[3-header->layer];
|
|
}
|
|
|
|
int frame_length(mp3header *header) {
|
|
return header->sync == 0xFFE ?
|
|
(frame_size_index[3-header->layer]*((header->version&1)+1)*
|
|
header_bitrate(header)/header_frequency(header))+
|
|
header->padding : 1;
|
|
}
|
|
|
|
int header_layer(mp3header *h) {return layer_tab[h->layer];}
|
|
|
|
int header_bitrate(mp3header *h) {
|
|
return bitrate[h->version & 1][3-h->layer][h->bitrate];
|
|
}
|
|
|
|
int header_frequency(mp3header *h) {
|
|
return frequencies[h->version][h->freq];
|
|
}
|
|
|
|
char *header_emphasis(mp3header *h) {
|
|
return emphasis_text[h->emphasis];
|
|
}
|
|
|
|
char *header_mode(mp3header *h) {
|
|
return mode_text[h->mode];
|
|
}
|
|
|
|
int sameConstant(mp3header *h1, mp3header *h2) {
|
|
if((*(uint*)h1) == (*(uint*)h2)) return 1;
|
|
|
|
if((h1->version == h2->version ) &&
|
|
(h1->layer == h2->layer ) &&
|
|
(h1->crc == h2->crc ) &&
|
|
(h1->freq == h2->freq ) &&
|
|
(h1->mode == h2->mode ) &&
|
|
(h1->copyright == h2->copyright ) &&
|
|
(h1->original == h2->original ) &&
|
|
(h1->emphasis == h2->emphasis ))
|
|
return 1;
|
|
else return 0;
|
|
}
|
|
|
|
|
|
int get_id3(mp3info *mp3) {
|
|
int retcode=0;
|
|
char fbuf[4];
|
|
|
|
if(mp3->datasize >= 128) {
|
|
if(fseek(mp3->file, -128, SEEK_END )) {
|
|
fprintf(stderr,"ERROR: Couldn't read last 128 bytes of %s!!\n",mp3->filename);
|
|
retcode |= 4;
|
|
} else {
|
|
fread(fbuf,1,3,mp3->file); fbuf[3] = '\0';
|
|
mp3->id3.genre[0]=255;
|
|
|
|
if (!strcmp((const char *)"TAG",(const char *)fbuf)) {
|
|
mp3->id3_isvalid=1;
|
|
mp3->datasize -= 128;
|
|
fseek(mp3->file, -125, SEEK_END);
|
|
fread(mp3->id3.title,1,30,mp3->file); mp3->id3.title[30] = '\0';
|
|
fread(mp3->id3.artist,1,30,mp3->file); mp3->id3.artist[30] = '\0';
|
|
fread(mp3->id3.album,1,30,mp3->file); mp3->id3.album[30] = '\0';
|
|
fread(mp3->id3.year,1,4,mp3->file); mp3->id3.year[4] = '\0';
|
|
fread(mp3->id3.comment,1,30,mp3->file); mp3->id3.comment[30] = '\0';
|
|
if(mp3->id3.comment[28] == '\0') {
|
|
mp3->id3.track[0] = mp3->id3.comment[29];
|
|
}
|
|
fread(mp3->id3.genre,1,1,mp3->file);
|
|
unpad(mp3->id3.title);
|
|
unpad(mp3->id3.artist);
|
|
unpad(mp3->id3.album);
|
|
unpad(mp3->id3.year);
|
|
unpad(mp3->id3.comment);
|
|
}
|
|
}
|
|
}
|
|
return retcode;
|
|
}
|
|
|
|
char *unpad(char *string) {
|
|
char *pos=string+strlen(string)-1;
|
|
while(isspace(pos[0])) (pos--)[0]=0;
|
|
return string;
|
|
}
|
|
|
|
} |