aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/Input/mad/decoder_mad.cpp
blob: c17f14f42895780e961ec153e53193b76e394e86 (plain) (tree)
1
2
3
4
5
6
                                                                            
                                                                            


                                                                            
                                                                            













                                                                            
                                                                            
                                                                             
 

                               

                  
                         
                        
 

                                                                 
                     
 
                                                 
 
                     
                    








                       
                  









                                



                         
             
                    

                                                 

                              
     



                             
                     
                    







                       
                 
     
                                                              
                     

     

                                                  
 
                           
     
                                                
         
                                                                            
                         


         
                                                    
     
                                        
                                           
                                              

     


                               
 
                      
     
                                                              
                     
     




                                                                               





                                                               

                    




                         
                  

               


                                 
 
                     
                    







                       
                  





























































                                                                           
                        
                  

                          


                                          
 
           
     
                          
                                                                   
         

                                 
                                     
             

                                                                      
             
 
                                                                                                  
 
                                   

                      

                                                                                                   

         
                                                      
         
                                                    
             

                                                                                          
                                
                                                        
                         
             
                                                                                           

                         
             
                                                                                               

                      
         
                      
 
                                    

                  



                                   


                                                                          
             
                              
 
                                                            
 

                                             
                                    







                                        
                                                         

                                                    
                              

                
                                           







                                                       
 
                
                     
 
                                            
     
                                                                 






                                                                        
     
 

                                                                    


                                        
                               
                     
                      
                

 
                              
 

                  
                       

 
                         
 
                          

 
                                                         
 
                     
                                     
             
 







                                                    
                                 


                       

                                                                      



                                          
                          
                                
                          

     
 

                             
                            
     

                                                                                   
     
                                                                                                     


                                          
                     


                    
                                      
                     

                         
                                                                               
                

 
                                                                           




                                                                            
                                                                                









                                                                 
                                                                                            
 


                                                             






















                                                                           

              













                               

                  






                                       

 

                                                                          
 



                                              



                               


                                                 

 













































                                                                                          
                                                              
 
                                               

                                    
                                             



                                              

                       
 
                                                 

                                                          
                                                  
     
 
                                 

                          




                                                                   


                                                         


                          




                                                                        


                                                             

         
                          
 

































                                                              
/***************************************************************************
 *  Based on mq3 and madplay projects                                      *
 *                                                                         *
 * Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>             *
 * Copyright (C) 2000-2004 Robert Leslie <rob@mars.org>                    *
 * Copyright (C) 2009-2013 Ilya Kotov forkotov02@hotmail.ru                *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/

#include <taglib/id3v2header.h>
#include <taglib/tbytevector.h>
#include <math.h>
#include <stdio.h>
#include "tagextractor.h"
#include "decoder_mad.h"

#define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
#define INPUT_BUFFER_SIZE (32*1024)
#define USE_DITHERING

DecoderMAD::DecoderMAD(QIODevice *i) : Decoder(i)
{
    m_inited = false;
    m_totalTime = 0;
    m_channels = 0;
    m_bitrate = 0;
    m_freq = 0;
    m_len = 0;
    m_input_buf = 0;
    m_input_bytes = 0;
    m_output_bytes = 0;
    m_output_at = 0;
    m_skip_frames = 0;
    m_eof = false;

    m_left_dither.random = 0;
    m_left_dither.error[0] = 0;
    m_left_dither.error[1] = 0;
    m_left_dither.error[2] = 0;

    m_right_dither.random = 0;
    m_right_dither.error[0] = 0;
    m_right_dither.error[1] = 0;
    m_right_dither.error[2] = 0;
}

DecoderMAD::~DecoderMAD()
{
    deinit();
    if (m_input_buf)
    {
        qDebug("DecoderMAD: deleting input_buf");
        delete [] m_input_buf;
        m_input_buf = 0;
    }
}

bool DecoderMAD::initialize()
{
    m_inited = false;
    m_totalTime = 0;
    m_channels = 0;
    m_bitrate = 0;
    m_freq = 0;
    m_len = 0;
    m_input_bytes = 0;
    m_output_bytes = 0;
    m_output_at = 0;

    if (!input())
    {
        qWarning("DecoderMAD: cannot initialize.  No input.");
        return false;
    }

    if (!m_input_buf)
        m_input_buf = new char[INPUT_BUFFER_SIZE];

    if (!input()->isOpen())
    {
        if (!input()->open(QIODevice::ReadOnly))
        {
            qWarning("DecoderMAD: %s", qPrintable(input()->errorString ()));
            return false;
        }
    }

    if (input()->isSequential ()) //for streams only
    {
        TagExtractor extractor(input());
        if(!extractor.id3v2tag().isEmpty())
            addMetaData(extractor.id3v2tag());
    }

    mad_stream_init(&m_stream);
    mad_frame_init(&m_frame);
    mad_synth_init(&m_synth);

    if (!findHeader())
    {
        qDebug("DecoderMAD: Can't find a valid MPEG header.");
        return false;
    }
    mad_stream_buffer(&m_stream, (unsigned char *) m_input_buf, m_input_bytes);
    m_stream.error = MAD_ERROR_BUFLEN;
    mad_frame_mute (&m_frame);
    m_stream.next_frame = 0;
    m_stream.sync = 0;
    ChannelMap map;
    if(m_channels == 1)
        map << Qmmp::CHAN_FRONT_LEFT;
    else
        map << Qmmp::CHAN_FRONT_LEFT << Qmmp::CHAN_FRONT_RIGHT;
    configure(m_freq, map, Qmmp::PCM_S16LE);
    m_inited = true;
    return true;
}


void DecoderMAD::deinit()
{
    if (!m_inited)
        return;

    mad_synth_finish(&m_synth);
    mad_frame_finish(&m_frame);
    mad_stream_finish(&m_stream);

    m_inited = false;
    m_totalTime = 0;
    m_channels = 0;
    m_bitrate = 0;
    m_freq = 0;
    m_len = 0;
    m_input_bytes = 0;
    m_output_bytes = 0;
    m_output_at = 0;
    m_skip_frames = 0;
    m_eof = false;
}

bool DecoderMAD::findXingHeader(struct mad_bitptr ptr, unsigned int bitlen)
{
    if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
        goto fail;

    xing.flags = mad_bit_read(&ptr, 32);
    bitlen -= 64;

    if (xing.flags & XING_FRAMES)
    {
        if (bitlen < 32)
            goto fail;

        xing.frames = mad_bit_read(&ptr, 32);
        bitlen -= 32;
    }

    if (xing.flags & XING_BYTES)
    {
        if (bitlen < 32)
            goto fail;

        xing.bytes = mad_bit_read(&ptr, 32);
        bitlen -= 32;
    }

    if (xing.flags & XING_TOC)
    {
        int i;

        if (bitlen < 800)
            goto fail;

        for (i = 0; i < 100; ++i)
            xing.toc[i] = mad_bit_read(&ptr, 8);

        bitlen -= 800;
    }

    if (xing.flags & XING_SCALE)
    {
        if (bitlen < 32)
            goto fail;

        xing.scale = mad_bit_read(&ptr, 32);
        bitlen -= 32;
    }

    return true;

fail:
    xing.flags = 0;
    xing.frames = 0;
    xing.bytes = 0;
    xing.scale = 0;
    return false;
}

bool DecoderMAD::findHeader()
{
    bool result = false;
    int count = 0;
    bool has_xing = false;
    bool is_vbr = false;
    mad_timer_t duration = mad_timer_zero;
    struct mad_header header;
    mad_header_init (&header);

    forever
    {
        m_input_bytes = 0;
        if (m_stream.error == MAD_ERROR_BUFLEN || !m_stream.buffer)
        {
            size_t remaining = 0;

            if (!m_stream.next_frame)
            {
                remaining = m_stream.bufend - m_stream.next_frame;
                memmove (m_input_buf, m_stream.next_frame, remaining);
            }

            m_input_bytes = input()->read(m_input_buf + remaining, INPUT_BUFFER_SIZE - remaining);

            if (m_input_bytes <= 0)
                break;

            mad_stream_buffer(&m_stream, (unsigned char *) m_input_buf + remaining, m_input_bytes);
            m_stream.error = MAD_ERROR_NONE;
        }

        if (mad_header_decode(&header, &m_stream) < 0)
        {
            if(m_stream.error == MAD_ERROR_LOSTSYNC)
            {
                uint tagSize = findID3v2((uchar *)m_stream.this_frame,
                                         (ulong) (m_stream.bufend - m_stream.this_frame));
                if (tagSize > 0)
                    mad_stream_skip(&m_stream, tagSize);
                continue;
            }
            else if (m_stream.error == MAD_ERROR_BUFLEN || MAD_RECOVERABLE(m_stream.error))
                continue;
            else
            {
                qDebug ("DecoderMAD: Can't decode header: %s", mad_stream_errorstr(&m_stream));
                break;
            }
        }
        result = true;

        if (input()->isSequential())
            break;

        count ++;
        //try to detect xing header
        if (count == 1)
        {
            m_frame.header = header;
            if (mad_frame_decode(&m_frame, &m_stream) != -1 &&
                    findXingHeader(m_stream.anc_ptr, m_stream.anc_bitlen))
            {
                is_vbr = true;

                qDebug ("DecoderMAD: Xing header detected");

                if (xing.flags & XING_FRAMES)
                {
                    has_xing = true;
                    count = xing.frames;
                    break;
                }
            }
        }
        //try to detect VBR
        if (!is_vbr && !(count > 15))
        {
            if (m_bitrate && header.bitrate != m_bitrate)
            {
                qDebug ("DecoderMAD: VBR detected");
                is_vbr = true;
            }
            else
                m_bitrate = header.bitrate;
        }
        else if (!is_vbr)
        {
            qDebug ("DecoderMAD: Fixed rate detected");
            break;
        }
        mad_timer_add (&duration, header.duration);
    }

    if (!result)
        return false;

    if (!is_vbr && !input()->isSequential())
    {
        double time = (input()->size() * 8.0) / (header.bitrate);
        double timefrac = (double)time - ((long)(time));
        mad_timer_set(&duration, (long)time, (long)(timefrac*100), 100);
    }
    else if (has_xing)
    {
        mad_timer_multiply (&header.duration, count);
        duration = header.duration;
    }

    m_totalTime = mad_timer_count(duration, MAD_UNITS_MILLISECONDS);
    qDebug ("DecoderMAD: Total time: %ld", long(m_totalTime));
    m_freq = header.samplerate;
    m_channels = MAD_NCHANNELS(&header);
    m_bitrate = header.bitrate / 1000;
    mad_header_finish(&header);
    input()->seek(0);
    m_input_bytes = 0;
    return true;
}

qint64 DecoderMAD::totalTime()
{
    if (!m_inited)
        return 0;
    return m_totalTime;
}

int DecoderMAD::bitrate()
{
    return int(m_bitrate);
}

qint64 DecoderMAD::read(unsigned char *data, qint64 size)
{
    if(decodeFrame())
        return madOutput(data, size);
    return 0;
}

qint64 DecoderMAD::read(float *data, qint64 samples)
{
    if(decodeFrame())
        return madOutputFloat(data, samples);
    return 0;
}

void DecoderMAD::seek(qint64 pos)
{
    if(m_totalTime > 0)
    {
        qint64 seek_pos = qint64(pos * input()->size() / m_totalTime);
        input()->seek(seek_pos);
        mad_frame_mute(&m_frame);
        mad_synth_mute(&m_synth);
        m_stream.error = MAD_ERROR_BUFLEN;
        m_stream.sync = 0;
        m_input_bytes = 0;
        m_stream.next_frame = 0;
        m_skip_frames = 2;
    }
}

bool DecoderMAD::fillBuffer()
{
    if (m_stream.next_frame)
    {
        m_input_bytes = &m_input_buf[m_input_bytes] - (char *) m_stream.next_frame;
        memmove(m_input_buf, m_stream.next_frame, m_input_bytes);
    }
    int len = input()->read((char *) m_input_buf + m_input_bytes, INPUT_BUFFER_SIZE - m_input_bytes);
    if (!len)
    {
        qDebug("DecoderMAD: end of file");
        return false;
    }
    else if(len < 0)
    {
        qWarning("DecoderMAD: error");
        return false;
    }
    m_input_bytes += len;
    mad_stream_buffer(&m_stream, (unsigned char *) m_input_buf, m_input_bytes);
    return true;
}

uint DecoderMAD::findID3v2(uchar *data, ulong size) //retuns ID3v2 tag size
{
    if (size < 10)
        return 0;

    if (((data[0] == 'I' && data[1] == 'D' && data[2] == '3') || //ID3v2 tag
         (data[0] == '3' && data[1] == 'D' && data[2] == 'I')) && //ID3v2 footer
            data[3] < 0xff && data[4] < 0xff && data[6] < 0x80 &&
            data[7] < 0x80 && data[8] < 0x80 && data[9] < 0x80)
    {
        TagLib::ByteVector byteVector((char *)data, size);
        TagLib::ID3v2::Header header(byteVector);
        return header.tagSize();
    }
    return 0;
}

unsigned long DecoderMAD::prng(unsigned long state) // 32-bit pseudo-random number generator
{
    return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}

long DecoderMAD::audio_linear_dither(unsigned int bits, mad_fixed_t sample,
                                     struct audio_dither *dither)
{
    unsigned int scalebits;
    mad_fixed_t output, mask, random;

    /* noise shape */
    sample += dither->error[0] - dither->error[1] + dither->error[2];

    dither->error[2] = dither->error[1];
    dither->error[1] = dither->error[0] / 2;

    /* bias */
    output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));

    scalebits = MAD_F_FRACBITS + 1 - bits;
    mask = (1L << scalebits) - 1;

    /* dither */
    random  = prng(dither->random);
    output += (random & mask) - (dither->random & mask);

    dither->random = random;

    /* clip */
    if (output > CLIP_MAX)
    {
        output = CLIP_MAX;

        if (sample > CLIP_MAX)
            sample = CLIP_MAX;
    }
    else if (output < CLIP_MIN)
    {
        output = CLIP_MIN;

        if (sample < CLIP_MIN)
            sample = CLIP_MIN;
    }

    /* quantize */
    output &= ~mask;

    /* error feedback */
    dither->error[0] = sample - output;

    /* scale */
    return output >> scalebits;
}

//generic linear sample quantize routine
long DecoderMAD::audio_linear_round(unsigned int bits, mad_fixed_t sample)
{
    /* round */
    sample += (1L << (MAD_F_FRACBITS - bits));

    /* clip */
    if (sample > CLIP_MAX)
        sample = CLIP_MAX;
    else if (sample < CLIP_MIN)
        sample = CLIP_MIN;

    /* quantize and scale */
    return sample >> (MAD_F_FRACBITS + 1 - bits);
}

bool DecoderMAD::decodeFrame()
{
    forever
    {
        if(((m_stream.error == MAD_ERROR_BUFLEN) || !m_stream.buffer) && !m_eof)
        {
            m_eof = !fillBuffer();
        }
        if(mad_frame_decode(&m_frame, &m_stream) < 0)
        {
            switch((int) m_stream.error)
            {
            case MAD_ERROR_LOSTSYNC:
            {
                //skip ID3v2 tag
                uint tagSize = findID3v2((uchar *)m_stream.this_frame,
                                         (ulong) (m_stream.bufend - m_stream.this_frame));
                if (tagSize > 0)
                {
                    mad_stream_skip(&m_stream, tagSize);
                    qDebug("DecoderMAD: %d bytes skipped", tagSize);
                }
                continue;
            }
            case MAD_ERROR_BUFLEN:
                if(m_eof)
                    return false;
                continue;
            default:
                if (!MAD_RECOVERABLE(m_stream.error))
                    return false;
                else
                    continue;
            }
        }
        if(m_skip_frames)
        {
            m_skip_frames--;
            continue;
        }
        mad_synth_frame(&m_synth, &m_frame);
        break;
    }
    return true;
}

qint64 DecoderMAD::madOutput(unsigned char *data, qint64 size)
{
    unsigned int samples_per_channel, channels;
    mad_fixed_t const *left, *right;

    samples_per_channel = m_synth.pcm.length;
    channels = m_synth.pcm.channels;
    left = m_synth.pcm.samples[0];
    right = m_synth.pcm.samples[1];
    m_bitrate = m_frame.header.bitrate / 1000;
    m_output_at = 0;
    m_output_bytes = 0;

    if(samples_per_channel * channels * 2 > size)
    {
        qWarning("DecoderMad: input buffer is too small");
        samples_per_channel = size / channels / 2;
    }

    while (samples_per_channel--)
    {
        signed int sample;
#ifdef USE_DITHERING
        sample = audio_linear_dither(16, *left++,  &m_left_dither);
#else
        sample = audio_linear_round(16, *left++);
#endif
        *(data + m_output_at++) = ((sample >> 0) & 0xff);
        *(data + m_output_at++) = ((sample >> 8) & 0xff);
        m_output_bytes += 2;

        if (channels == 2)
        {
#ifdef USE_DITHERING
            sample = audio_linear_dither(16, *right++, &m_right_dither);
#else
            sample = audio_linear_round(16, *right++);
#endif
            *(data + m_output_at++) = ((sample >> 0) & 0xff);
            *(data + m_output_at++) = ((sample >> 8) & 0xff);
            m_output_bytes += 2;
        }
    }
    return m_output_bytes;
}

qint64 DecoderMAD::madOutputFloat(float *data, qint64 samples)
{
    float *data_it = data;
    unsigned int samples_per_channel, channels;
    mad_fixed_t const *left, *right;

    samples_per_channel = m_synth.pcm.length;
    channels = m_synth.pcm.channels;
    left = m_synth.pcm.samples[0];
    right = m_synth.pcm.samples[1];
    m_bitrate = m_frame.header.bitrate / 1000;
    m_output_at = 0;
    m_output_bytes = 0;
    qint64 output_samples = 0;

    if(samples_per_channel * channels > samples)
    {
        qWarning("DecoderMad: input buffer is too small");
        samples_per_channel = samples / channels;
    }

    while (samples_per_channel--)
    {
        *data_it++ = mad_f_todouble(*left++);
        output_samples++;
        if (channels == 2)
        {
            *data_it++ = mad_f_todouble(*right++);
            output_samples++;
        }
    }
    return output_samples;
}