root/src/blocks/mp3.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. readMP3Header
  2. nextMP3Frame
  3. getMP3Flags
  4. getMP3Samples
  5. getMP3Duration

/*
    Ming, an SWF output library
    Copyright (C) 2002  Opaque Industries - http://www.opaque.net/

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

/* $Id: mp3.c,v 1.21 2008/07/22 20:38:42 krechert Exp $ */

#include "libming.h"
#include "input.h"
#include "mp3.h"
#include "error.h"
#include "sound.h"


// [version][idx]
static unsigned short mp3_samplingrate_tbl[4][4] =
{ 
        // mpeg 2.5
        { 11025, 12000, 8000, 0},
        // reserved
        { 0, 0, 0, 0},
        // mpeg 2
        { 22050, 24000, 16000, 0},
        // mpeg 1
        { 44100, 48000, 32000, 0}
};

// [version][layer][idx]
static unsigned short mp3_bitrate_tbl[4][4][16] = 
{
        // mpeg 2.5
        { 
                // reserved
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                // layer 3
                { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
                // layer 2
                { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
                // layer 1
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        
        // reserved
        { 
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },

        // mpeg version 2
        {
                // reserved
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                // layer 3
                { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
                // layer 2
                { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
                // layer 1
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

        },
        // mpeg version 1
        { 
                // reserved
                { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
                // layer 3      
                { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
                // layer 2
                { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 },
                // layer 1
                { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 382, 416, 448, 0}
        }
};


#define CHECK_STREAM(__in)                      \
        do {                                    \
                if(SWFInput_eof(__in))          \
                        return 0;               \
        } while(0);

        
/*
 * reads a MP3 header 
 * returns:
 *      0 if EOF
 *      -1 if invalid header; stream is unaltered
 *      1 on success
 */     
int readMP3Header(SWFInput input, struct mp3_header *mp3h)
{
        int frameSync, br, sr;
        if(SWFInput_length(input) - SWFInput_tell(input) < 4)
                return 0;
        
        frameSync = SWFInput_readBits(input, 11);
        CHECK_STREAM(input);

        mp3h->version = SWFInput_readBits(input, 2);
        mp3h->layer = SWFInput_readBits(input, 2);
        SWFInput_readBits(input, 1); // protect bit
        CHECK_STREAM(input);

        br = SWFInput_readBits(input, 4);
        sr = SWFInput_readBits(input, 2);
        mp3h->bitrate = mp3_bitrate_tbl[mp3h->version][mp3h->layer][br];
        mp3h->samplingRate = mp3_samplingrate_tbl[mp3h->version][sr];
        mp3h->padding = SWFInput_readBits(input, 1);
        SWFInput_readBits(input, 1); // private bit
        CHECK_STREAM(input);
        
        mp3h->channelMode = SWFInput_readBits(input, 2);
        SWFInput_readBits(input, 2); // extension
        SWFInput_readBits(input, 3); // copyright, orig, emphasis
        CHECK_STREAM(input);
        SWFInput_byteAlign(input);
        if((frameSync & 0x7ff) != 0x7ff)
        {
                SWFInput_seek(input, -4, SEEK_CUR);
                return -1;
        }

        if(mp3h->version == 1 || mp3h->layer == 0)
        {
                SWFInput_seek(input, -4, SEEK_CUR);  
                return -1;
        }
        return 1;
}

int nextMP3Frame(SWFInput input)
{
        int frameLen, ret;
        struct mp3_header mp3h;

        ret = readMP3Header(input, &mp3h);
        
        if(ret < 0)
                return -1;
        
        if(ret == 0 || SWFInput_eof(input))
                return 0;
        
        if(mp3h.samplingRate == 0 || mp3h.bitrate == 0)
                SWF_error("invalid mp3 file\n");
        if(mp3h.version == MP3_VERSION_1)
        {
                frameLen = 144 * mp3h.bitrate * 1000 
                                / mp3h.samplingRate + mp3h.padding;
        }
        else
                frameLen = 72 * mp3h.bitrate * 1000 
                        / mp3h.samplingRate + mp3h.padding;

        SWFInput_seek(input, frameLen-4, SEEK_CUR);
        return frameLen;
}

/*
 * returns the sound flags used within ming
 * stream is set to the first mp3 frame header.
 */
int getMP3Flags(SWFInput input, byte *flags)
{
        struct mp3_header mp3h; 
        int rate=0, channels, start = 0;
        int ret;

        /* 
         * skip stream until first MP3 header which starts with 0xffe 
         */
        while((ret = readMP3Header(input, &mp3h)) < 0)
        {
                SWFInput_seek(input, 1, SEEK_CUR);
                start++;
        }

        if(ret == 0 || SWFInput_eof(input))
                return -1;

        SWFInput_seek(input, start, SEEK_SET);
        if (mp3h.channelMode == MP3_CHANNEL_MONO )
                channels = SWF_SOUND_MONO;
        else
                channels = SWF_SOUND_STEREO;

        switch ( mp3h.version )
        {
                case MP3_VERSION_1:
                        rate = SWF_SOUND_44KHZ; 
                        break;

                case MP3_VERSION_2:
                        rate = SWF_SOUND_22KHZ; 
                        break;

                case MP3_VERSION_25:
                        rate = SWF_SOUND_11KHZ; 
                        break;
        }

        *flags =
                SWF_SOUND_MP3_COMPRESSED | rate | SWF_SOUND_16BITS | channels;
        return start;   
}

int getMP3Samples(SWFInput input, int flags, int *wanted)
{
        int frameSize, length;
        int numSamples = 0;
        int totalLength = 0;

        switch(flags & SWF_SOUND_RATE)
        {
        case SWF_SOUND_44KHZ:
                frameSize = 1152;
                break;
        case SWF_SOUND_22KHZ:
        case SWF_SOUND_11KHZ:
                frameSize = 576;
                break;
        default:
                *wanted = 0;
                return -1;
        }

        while(*wanted < 0 || numSamples < *wanted - frameSize) 
        {
                length = nextMP3Frame(input);
                if(length <= 0)
                        break;  
                totalLength += length;
                numSamples += frameSize;
        }
        *wanted = numSamples;                   
        return totalLength;
}

/*
 * Returns the length of a mp3 stream in ms. The duration is
 * measured in ms. The stream is unaltered.
 */
unsigned int getMP3Duration(SWFInput input)
{
        int start, flags, samples = -1;
        int sampleRate;

        start = SWFInput_tell(input);
        if(getMP3Flags(input, (byte *)&flags) < 0)
                return 0;
        if(getMP3Samples(input, flags, &samples) <= 0)
                return 0;
        
        sampleRate = SWFSound_getSampleRate(flags);
        SWFInput_seek(input, start, SEEK_SET);
        return samples * 1000.0 / sampleRate;
}
/*
 * Local variables:
 * tab-width: 2
 * c-basic-offset: 2
 * End:
 */

/* [<][>][^][v][top][bottom][index][help] */