Logo Search packages:      
Sourcecode: qmmp version File versions

decoder_vorbis.cpp

// 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/constants.h>
#include <qmmp/buffer.h>
#include <qmmp/output.h>
#include <qmmp/recycler.h>
#include <qmmp/filetag.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, 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 *src)
{
    DecoderVorbis *dogg = (DecoderVorbis *) src;
    dogg->input()->close();
    return 0;
}


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


// Decoder class

DecoderVorbis::DecoderVorbis(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
        : Decoder(parent, d, i, o)
{
    inited = FALSE;
    user_stop = FALSE;
    stat = 0;
    output_buf = 0;
    output_bytes = 0;
    output_at = 0;
    bks = 0;
    done = FALSE;
    finish = FALSE;
    len = 0;
    freq = 0;
    bitrate = 0;
    seekTime = -1.0;
    totalTime = 0.0;
    chan = 0;
    output_size = 0;
}


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

    if (output_buf)
        delete [] output_buf;
    output_buf = 0;
}


void DecoderVorbis::stop()
{
    user_stop = TRUE;
}


void DecoderVorbis::flush(bool final)
{
    ulong min = final ? 0 : bks;

    while ((! done && ! finish) && output_bytes > min)
    {
        output()->recycler()->mutex()->lock ();

        while ((! done && ! finish) && output()->recycler()->full())
        {
            mutex()->unlock();

            output()->recycler()->cond()->wait(output()->recycler()->mutex());

            mutex()->lock ();
            done = user_stop;
        }

        if (user_stop || finish)
        {
            inited = FALSE;
            done = TRUE;
        }
        else
        {
            /*ulong sz = output_bytes < bks ? output_bytes : bks;
            Buffer *b = output()->recycler()->get();

            memcpy(b->data, output_buf, sz);
            if (sz != bks) memset(b->data + sz, 0, bks - sz);

            b->nbytes = bks;
            b->rate = bitrate;
            output_size += b->nbytes;
            output()->recycler()->add();

            output_bytes -= sz;
            memmove(output_buf, output_buf + sz, output_bytes);*/
            output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
            output_size += bks;
            output_at = output_bytes;
        }

        if (output()->recycler()->full())
        {
            output()->recycler()->cond()->wakeOne();
        }

        output()->recycler()->mutex()->unlock();
    }
}


bool DecoderVorbis::initialize()
{
    qDebug("DecoderVorbis: initialize");
    bks = blockSize();

    inited = user_stop = done = finish = FALSE;
    len = freq = bitrate = 0;
    stat = chan = 0;
    output_size = 0;
    seekTime = -1.0;
    totalTime = 0.0;
    if (! input())
    {
        qDebug("DecoderVorbis: cannot initialize.  No input");

        return FALSE;
    }

    if (! output_buf)
        output_buf = new char[globalBufferSize];
    output_at = 0;
    output_bytes = 0;

    if (! input()->isOpen())
    {
        if (! input()->open(QIODevice::ReadOnly))
        {
            qWarning(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;
    }

    freq = 0;
    bitrate = ov_bitrate(&oggfile, -1) / 1000;
    chan = 0;

    totalTime = long(ov_time_total(&oggfile, 0));
    totalTime = totalTime < 0 ? 0 : totalTime;

    vorbis_info *ogginfo = ov_info(&oggfile, -1);
    if (ogginfo)
    {
        freq = ogginfo->rate;
        chan = ogginfo->channels;
    }

    configure(freq, chan, 16, bitrate);

    inited = TRUE;
    return TRUE;
}


double DecoderVorbis::lengthInSeconds()
{
    if (! inited)
        return 0;

    return totalTime;
}


void DecoderVorbis::seek(double pos)
{
    seekTime = pos;
}


void DecoderVorbis::deinit()
{
    if (inited)
        ov_clear(&oggfile);
    inited = user_stop = done = finish = FALSE;
    len = freq = bitrate = 0;
    stat = chan = 0;
    output_size = 0;
}

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

    FileTag tag;
    comments = ov_comment (&oggfile, -1);
    for (i = 0; i < comments->comments; i++)
    {
        if (!strncasecmp(comments->user_comments[i], "title=",
                         strlen ("title=")))
            tag.setValue(FileTag::TITLE, QString::fromUtf8(comments->user_comments[i]
                         + strlen ("title=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "artist=", strlen ("artist=")))
            tag.setValue(FileTag::ARTIST,
                         QString::fromUtf8(comments->user_comments[i]
                                           + strlen ("artist=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "album=", strlen ("album=")))
            tag.setValue(FileTag::ALBUM,
                         QString::fromUtf8(comments->user_comments[i]
                                           + strlen ("album=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "comment=", strlen ("comment=")))
            tag.setValue(FileTag::COMMENT,
                         QString::fromUtf8(comments->user_comments[i]
                                           + strlen ("comment=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "genre=", strlen ("genre=")))
            tag.setValue(FileTag::GENRE, QString::fromUtf8 (comments->user_comments[i]
                         + strlen ("genre=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "tracknumber=",
                              strlen ("tracknumber=")))
            tag.setValue(FileTag::TRACK, atoi (comments->user_comments[i]
                                               + strlen ("tracknumber=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "track=", strlen ("track=")))
            tag.setValue(FileTag::TRACK, atoi (comments->user_comments[i]
                                               + strlen ("track=")));
        else if (!strncasecmp(comments->user_comments[i],
                              "date=", strlen ("date=")))
            tag.setValue(FileTag::YEAR, atoi (comments->user_comments[i]
                                               + strlen ("date=")));

    }
    tag.setValue(FileTag::LENGTH, uint(totalTime));
    dispatch(tag);
}

void DecoderVorbis::run()
{
    mutex()->lock ();

    if (! inited)
    {
        mutex()->unlock();

        return;
    }

    //stat = DecoderEvent::Decoding;
    stat = DecoderState::Decoding;
    mutex()->unlock();

    {
        //DecoderEvent e((DecoderEvent::Type) stat);
        //dispatch(e);
        //DecoderStatus st ((DecoderStatus::Type) stat);
        dispatch(DecoderState ((DecoderState::Type) stat));

        //emit statusChanged(stat);
    }

    int section = 0;
    int last_section = -1;

    while (! done && ! finish)
    {
        mutex()->lock ();
        // decode

        if (seekTime >= 0.0)
        {
            ov_time_seek(&oggfile, double(seekTime));
            seekTime = -1.0;

            output_size = long(ov_time_tell(&oggfile)) * long(freq * chan * 2);
        }
        len = -1;
        while (len < 0)
        {
            len = ov_read(&oggfile, (char *) (output_buf + output_at), bks, 0, 2, 1,
                          &section);
        }
        if (section != last_section)
            updateTags();
        last_section = section;

        if (len > 0)
        {
            bitrate = ov_bitrate_instant(&oggfile) / 1000;

            output_at += len;
            output_bytes += len;

            if (output())
                flush();
        }
        else if (len == 0)
        {
            flush(TRUE);

            if (output())
            {
                output()->recycler()->mutex()->lock ();
                // end of stream
                while (! output()->recycler()->empty() && ! user_stop)
                {
                    output()->recycler()->cond()->wakeOne();
                    mutex()->unlock();
                    output()->recycler()->cond()->wait(output()->recycler()->mutex());
                    mutex()->lock ();
                }
                output()->recycler()->mutex()->unlock();
            }

            done = TRUE;
            if (! user_stop)
            {
                finish = TRUE;
            }
        }
        else
        {
            // error in read
            error("DecoderVorbis: Error while decoding stream, File appears to be "
                  "corrupted");

            finish = TRUE;
        }

        mutex()->unlock();
    }

    mutex()->lock ();

    if (finish)
        stat = DecoderState::Finished;
    else if (user_stop)
        stat = DecoderState::Stopped;

    mutex()->unlock();

    {
        /*DecoderEvent e((DecoderEvent::Type) stat);
        dispatch(e);*/
        //DecoderStatus st ((DecoderStatus::Type) stat);
        //emit statusChanged(st);
        dispatch(DecoderState ((DecoderState::Type) stat));
    }

    deinit();
}

Generated by  Doxygen 1.6.0   Back to index