aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/Input/vorbis/decoder_vorbis.cpp
blob: ce3f2d28c0356b67d9bd32d0b9df4c2f09f76fda (plain) (tree)
1
2
3
4
5
6
7
8
9





                                                                    
 

                        
                          
                   

                    
                           
 
                             








                                                                       
                                                             



























                                                
                           
 













                                                
                                                              
                    
 
                   



                        
                





                               

 

                                
                                        
                   

                    
     
                                                              
                     

     
                           
     
                                                
         
                                                                              
                                                                
                         



                        





                 

                                                              
                                                      
 
                     

     


                                                
 

                                                              






                                                 
                                           

                  


 
                                 
 
                
                 
                       

 
                            
 
                     




                            
               
                           
            

 




                                
                                            




                                                              

                                                                                     

                                                             


                                                                        

                                                           


                                                                        
                                                         
                                                               


                                                                        

                                                           

                                                                                      
                                                         

                                                       

                                                                                        

                                                           

                                                                                        

                                                         

                                                                                       







                                                                                             
     
                                      
                          

 
                                     
 

                                               
 
                                                      


                   

                                                                    





                                                        
 




























                                                                 
// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
//
// Use, modification and distribution is allowed without limitation,
// warranty, or liability of any kind.
//


#include <qmmp/buffer.h>
#include <qmmp/output.h>
#include <qmmp/fileinfo.h>
#include <stdlib.h>
#include <QObject>
#include <QIODevice>
#include "decoder_vorbis.h"

// ic functions for OggVorbis
static size_t oggread (void *buf, size_t size, size_t nmemb, void *src)
{
    if (! src) return 0;

    DecoderVorbis *dogg = (DecoderVorbis *) src;
    int len = dogg->input()->read((char *) buf, (size * nmemb));
    return len / size;
}

static int oggseek(void *src, ogg_int64_t offset, int whence)
{
    DecoderVorbis *dogg = (DecoderVorbis *) src;

    if ( dogg->input()->isSequential ())
        return -1;

    long start = 0;
    switch (whence)
    {
    case SEEK_END:
        start = dogg->input()->size();
        break;

    case SEEK_CUR:
        start = dogg->input()->pos();
        break;

    case SEEK_SET:
    default:
        start = 0;
    }

    if (dogg->input()->seek(start + offset))
        return 0;
    return -1;
}


static int oggclose(void *)
{
    return 0;
}


static long oggtell(void *src)
{
    DecoderVorbis *dogg = (DecoderVorbis *) src;
    long t = dogg->input()->pos();
    return t;
}


// Decoder class

DecoderVorbis::DecoderVorbis(const QString &url, QIODevice *i)
        : Decoder(i)
{
    inited = false;
    m_totalTime = 0;
    m_section = 0;
    m_last_section = -1;
    m_bitrate = 0;
    m_url = url;
}


DecoderVorbis::~DecoderVorbis()
{
    deinit();
}

bool DecoderVorbis::initialize()
{
    qDebug("DecoderVorbis: initialize");
    inited = false;
    m_totalTime = 0;
    if (!input())
    {
        qDebug("DecoderVorbis: cannot initialize.  No input");
        return false;
    }

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

    ov_callbacks oggcb =
    {
        oggread,
        oggseek,
        oggclose,
        oggtell
    };
    if (ov_open_callbacks(this, &oggfile, NULL, 0, oggcb) < 0)
    {
        qWarning("DecoderVorbis: cannot open stream");

        return false;
    }

    quint32 freq = 0;
    m_bitrate = ov_bitrate(&oggfile, -1) / 1000;
    int chan = 0;

    if((m_totalTime = ov_time_total(&oggfile, -1) * 1000) < 0)
        m_totalTime = 0;

    vorbis_info *ogginfo = ov_info(&oggfile, -1);
    if (ogginfo)
    {
        freq = ogginfo->rate;
        chan = ogginfo->channels;
    }
    configure(freq, chan, Qmmp::PCM_S16LE);
    inited = true;
    return true;
}


qint64 DecoderVorbis::totalTime()
{
    if (!inited)
        return 0;
    return m_totalTime;
}

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


void DecoderVorbis::deinit()
{
    if (inited)
        ov_clear(&oggfile);
    len = 0;
}

void DecoderVorbis::updateTags()
{
    int i;
    vorbis_comment *comments;

    QMap <Qmmp::MetaData, QString> metaData;
    comments = ov_comment (&oggfile, -1);
    for (i = 0; i < comments->comments; i++)
    {
        if (!strncasecmp(comments->user_comments[i], "title=",
                         strlen ("title=")))
            metaData.insert(Qmmp::TITLE, QString::fromUtf8(comments->user_comments[i]
                            + strlen ("title=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "artist=", strlen ("artist=")))
            metaData.insert(Qmmp::ARTIST,
                            QString::fromUtf8(comments->user_comments[i]
                                              + strlen ("artist=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "album=", strlen ("album=")))
            metaData.insert(Qmmp::ALBUM,
                            QString::fromUtf8(comments->user_comments[i]
                                              + strlen ("album=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "comment=", strlen ("comment=")))
            metaData.insert(Qmmp::COMMENT,
                            QString::fromUtf8(comments->user_comments[i]
                                              + strlen ("comment=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "genre=", strlen ("genre=")))
            metaData.insert(Qmmp::GENRE, QString::fromUtf8 (comments->user_comments[i]
                            + strlen ("genre=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "tracknumber=",
                              strlen ("tracknumber=")))
            metaData.insert(Qmmp::TRACK, QString::number(atoi(comments->user_comments[i]
                            + strlen ("tracknumber="))));
        else if (!strncasecmp(comments->user_comments[i],
                              "track=", strlen ("track=")))
            metaData.insert(Qmmp::TRACK, QString::number(atoi(comments->user_comments[i]
                            + strlen ("track="))));
        else if (!strncasecmp(comments->user_comments[i],
                              "date=", strlen ("date=")))
            metaData.insert(Qmmp::YEAR, QString::number(atoi(comments->user_comments[i]
                            + strlen ("date="))));
        else if (!strncasecmp(comments->user_comments[i],
                              "composer=", strlen ("composer=")))
            metaData.insert(Qmmp::COMPOSER, QString::fromUtf8 (comments->user_comments[i]
                            + strlen ("composer=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "discnumber=", strlen ("discnumber=")))
            metaData.insert(Qmmp::DISCNUMBER, QString::number(atoi(comments->user_comments[i]
                            + strlen ("discnumber="))));
    }
    metaData.insert(Qmmp::URL, m_url);
    addMetaData(metaData);
}

void DecoderVorbis::seek(qint64 time)
{
    ov_time_seek(&oggfile, (double) time/1000);
}

qint64 DecoderVorbis::read(char *data, qint64 maxSize)
{
    len = -1;
    while (len < 0)
        len = ov_read(&oggfile, data, maxSize, 0, 2, 1, &m_section);

    if (m_section != m_last_section)
        updateTags();
    m_last_section = m_section;
    if(len > 0)
        m_bitrate = ov_bitrate_instant(&oggfile) / 1000;
    return len;
}

qint64 DecoderVorbis::read(float *data, qint64 samples)
{
    len = -1;
    float **pcm = 0;
    while (len < 0)
        len = ov_read_float(&oggfile, &pcm, samples, &m_section);

    if(len == 0)
        return 0;

    int channels = audioParameters().channels();

    for(int i = 0; i < channels; ++i)
    {
        float *ptr = &data[i];
        for(int j = 0; j < len; ++j)
        {
            *ptr = pcm[i][j];
            ptr += channels;
        }
    }

    if (m_section != m_last_section)
        updateTags();

    m_bitrate = ov_bitrate_instant(&oggfile) / 1000;
    return len*channels;
}