aboutsummaryrefslogblamecommitdiff
path: root/src/plugins/General/rgscan/rgscaner.cpp
blob: 2f1d5cc01f53703ceb24807404d5a1cd42f596f1 (plain) (tree)




















                                                                             
                   
                      
                  
                 

                                    

                     
                    
 
                
                

                         


                  

 


                     














                                     



                                          
                










































                                                                                         
                        




                     
                   

                       






                          
                             

                  

 




                             




                                

                    

                   
                        


                                                                        
                                                      


                                             
                                                                         

                                                          
                                                                                          
                              

                       
 
                
                                     
                                                 


           






                                                           
                                  
             














                                                            


             
         



                                                                             
 
                                            
 
                                  
             
                                                       
                 














































                                                                                        
                 
             
         
 




                                                                                          
 







                             
 
                                    

                                          
                                                                        
                       
                         
                         
 
/***************************************************************************
 *   Copyright (C) 2013 by 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 <stdio.h>
#include <stdint.h>
#include <QStringList>
#include <QThread>
#include <math.h>
#include <qmmp/inputsourcefactory.h>
#include <qmmp/decoderfactory.h>
#include "rgscaner.h"

RGScaner::RGScaner()
{
    m_gain = 0.;
    m_peak = 0.;
    m_user_stop = false;
    m_is_running = false;
    m_handle = 0;
    m_decoder = 0;
    m_source = 0;
}

RGScaner::~RGScaner()
{
    stop();
    if(m_handle)
    {
        DeinitGainAbalysis(m_handle);
        m_handle = 0;
    }
    if(m_decoder)
    {
        delete m_decoder;
        m_decoder = 0;
    }
    if(m_source)
    {
        delete m_source;
        m_source = 0;
    }
}

bool RGScaner::prepare(const QString &url)
{
    m_url = url;
    InputSource *source = InputSource::create(url, 0);
    if(!source->initialize())
    {
        delete source;
        qWarning("RGScaner: Invalid url");
        return false;
    }

    if(source->ioDevice())
    {
        if(!source->ioDevice()->open(QIODevice::ReadOnly))
        {
            delete source;
            qWarning("RGScaner: unable to open input stream, error: %s",
                     qPrintable(m_source->ioDevice()->errorString()));
            return false;
        }
    }

    DecoderFactory *factory = Decoder::findByPath(source->url());

    if(!factory)
    {
        qWarning("RGScaner: unsupported file format");
        delete source;
        return false;
    }
    qDebug("RGScaner: selected decoder: %s",qPrintable(factory->properties().shortName));

    if(factory->properties().noInput && source->ioDevice())
        source->ioDevice()->close();


    Decoder *decoder = factory->create(source->url(), source->ioDevice());
    if(!decoder->initialize())
    {
        qWarning("RGScaner: invalid file format");
        delete source;
        delete decoder;
        return false;
    }
    m_decoder = decoder;
    m_source = source;
    m_user_stop = false;
    return true;
}

void RGScaner::stop()
{
    m_mutex.lock();
    m_user_stop = true;
    m_mutex.unlock();
}

bool RGScaner::isRunning()
{
    return m_is_running;
}

double RGScaner::gain() const
{
    return m_gain;
}

double RGScaner::peak() const
{
    return m_peak;
}

GainHandle_t *RGScaner::handle()
{
    return m_handle;
}

void RGScaner::run()
{
    if(m_user_stop)
        return;
    m_is_running = true;
    qDebug("RGScaner: staring thread %lu",  QThread::currentThreadId());
    m_user_stop = false;

    AudioParameters ap = m_decoder->audioParameters();
    Qmmp::AudioFormat format = ap.format();
    bool headroom = m_decoder->hasHeadroom();
    const int buf_size = 8192; //samples
    double out_left[buf_size], out_right[buf_size]; //replay gain buffers
    float float_buf[buf_size]; //float buffer
    char char_buf[buf_size*ap.sampleSize()]; //char buffer
    qint64 totalSamples = m_decoder->totalTime() * ap.sampleRate() * ap.channels() / 1000;
    qint64 sample_counter = 0;
    qint64 samples = 0;
    double max = 0;

    if(m_handle)
        DeinitGainAbalysis(m_handle);
    InitGainAnalysis(&m_handle, ap.sampleRate());

    forever
    {
        if(headroom)
        {
            samples = m_decoder->read(float_buf, buf_size);

            if(samples <= 0)
                break;          //TODO add error handler

            if(ap.channels() == 2)
            {
                for(int i = 0; i < (samples >> 1); ++i)
                {
                    out_left[i] = float_buf[i*2]*32768.0;
                    out_right[i] = float_buf[i*2+1]*32768.0;
                    max = qMax(fabs(out_left[i]), max);
                    max = qMax(fabs(out_right[i]), max);
                }
            }
            else if(ap.channels() == 1)
            {
                for(int i = 0; i < samples; ++i)
                {
                    out_left[i] = float_buf[i]*32768.0;
                    max = qMax(fabs(out_left[i]), max);
                }
            }
        }
        else
        {
            qint64 len = m_decoder->read(char_buf, buf_size*ap.sampleSize());

            if(len <= 0)
                break;          //TODO add error handler

            samples = len / ap.sampleSize();

            if(ap.channels() == 2)
            {
                for(int i = 0; i < (samples >> 1); ++i)
                {
                    switch (format)
                    {
                    case Qmmp::PCM_S8:
                        out_left[i] = char_buf[i*2]*32768.0/128.0;
                        out_right[i] = char_buf[i*2+1]*32768.0/128.0;
                        break;
                    case Qmmp::PCM_S16LE:
                        out_left[i] = ((short *)char_buf)[i*2];
                        out_right[i] = ((short *)char_buf)[i*2+1];
                        break;
                    case Qmmp::PCM_S24LE:
                        out_left[i] = ((qint32 *)char_buf)[i*2]*32768.0/((1U << 23));
                        out_right[i] = ((qint32 *)char_buf)[i*2+1]*32768.0/((1U << 23));
                        break;
                    case Qmmp::PCM_S32LE:
                        out_left[i] = ((qint32 *)char_buf)[i*2]*32768.0/((1U << 31));
                        out_right[i] = ((qint32 *)char_buf)[i*2+1]*32768.0/((1U << 31));
                        break;
                    default:
                        break;
                    }
                    max = qMax(fabs(out_left[i]), max);
                    max = qMax(fabs(out_right[i]), max);
                }
            }
            else if(ap.channels() == 1)
            {
                for(int i = 0; i < samples; ++i)
                {
                    switch (format)
                    {
                    case Qmmp::PCM_S8:
                        out_left[i] = char_buf[i*2]*32768.0/128.0;
                        break;
                    case Qmmp::PCM_S16LE:
                        out_left[i] = ((short *)char_buf)[i*2];
                        break;
                    case Qmmp::PCM_S24LE:
                        out_left[i] = ((qint32 *)char_buf)[i*2]*32768.0/((1U << 23));
                        break;
                    case Qmmp::PCM_S32LE:
                        out_left[i] = ((qint32 *)char_buf)[i*2]*32768.0/((1U << 31));
                        break;
                    default:
                        break;
                    }
                    max = qMax(fabs(out_left[i]), max);
                }
            }
        }

        size_t samples_per_channel = samples >> ((ap.channels() == 2) ? 1 : 0);

        AnalyzeSamples(m_handle, out_left, out_right, samples_per_channel, ap.channels());
        sample_counter += samples;
        emit progress(100 * sample_counter / totalSamples);

        m_mutex.lock();
        if(m_user_stop)
        {
            m_mutex.unlock();
            break;
        }
        m_mutex.unlock();
    }

    m_gain = GetTitleGain(m_handle);
    m_peak = max/32768.0;
    qDebug("RGScaner: peak = %f", m_peak);
    qDebug("RGScaner: thread %lu finished", QThread::currentThreadId());
    emit progress(100);
    emit finished(m_url);
    m_is_running = false;
}